diff options
Diffstat (limited to 'src/core/lib')
139 files changed, 6748 insertions, 3747 deletions
diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc index 444bb3d719..b02c5b9fb6 100644 --- a/src/core/lib/gpr/arena.cc +++ b/src/core/lib/gpr/arena.cc @@ -26,6 +26,49 @@ #include <grpc/support/atm.h> #include <grpc/support/log.h> +// Uncomment this to use a simple arena that simply allocates the +// requested amount of memory for each call to gpr_arena_alloc(). This +// effectively eliminates the efficiency gain of using an arena, but it +// may be useful for debugging purposes. +//#define SIMPLE_ARENA_FOR_DEBUGGING + +#ifdef SIMPLE_ARENA_FOR_DEBUGGING + +#include <grpc/support/sync.h> + +struct gpr_arena { + gpr_mu mu; + void** ptrs; + size_t num_ptrs; +}; + +gpr_arena* gpr_arena_create(size_t ignored_initial_size) { + gpr_arena* arena = (gpr_arena*)gpr_zalloc(sizeof(*arena)); + gpr_mu_init(&arena->mu); + return arena; +} + +size_t gpr_arena_destroy(gpr_arena* arena) { + gpr_mu_destroy(&arena->mu); + for (size_t i = 0; i < arena->num_ptrs; ++i) { + gpr_free(arena->ptrs[i]); + } + gpr_free(arena->ptrs); + gpr_free(arena); + return 1; // Value doesn't matter, since it won't be used. +} + +void* gpr_arena_alloc(gpr_arena* arena, size_t size) { + gpr_mu_lock(&arena->mu); + arena->ptrs = + (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1)); + void* retval = arena->ptrs[arena->num_ptrs++] = gpr_zalloc(size); + gpr_mu_unlock(&arena->mu); + return retval; +} + +#else // SIMPLE_ARENA_FOR_DEBUGGING + // TODO(roth): We currently assume that all callers need alignment of 16 // bytes, which may be wrong in some cases. As part of converting the // arena API to C++, we should consider replacing gpr_arena_alloc() with a @@ -105,3 +148,5 @@ void* gpr_arena_alloc(gpr_arena* arena, size_t size) { ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)); return ptr + start - z->size_begin; } + +#endif // SIMPLE_ARENA_FOR_DEBUGGING diff --git a/src/core/lib/gpr/cpu_linux.cc b/src/core/lib/gpr/cpu_linux.cc index fda28916f8..9fc2f0b141 100644 --- a/src/core/lib/gpr/cpu_linux.cc +++ b/src/core/lib/gpr/cpu_linux.cc @@ -71,6 +71,10 @@ unsigned gpr_cpu_current_cpu(void) { gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); return 0; } + if (static_cast<unsigned>(cpu) >= gpr_cpu_num_cores()) { + gpr_log(GPR_ERROR, "Cannot handle hot-plugged CPUs"); + return 0; + } return static_cast<unsigned>(cpu); #endif } diff --git a/src/core/lib/gpr/cpu_posix.cc b/src/core/lib/gpr/cpu_posix.cc index 7a77f7ab64..915fd4976c 100644 --- a/src/core/lib/gpr/cpu_posix.cc +++ b/src/core/lib/gpr/cpu_posix.cc @@ -37,7 +37,7 @@ static long ncpus = 0; static pthread_key_t thread_id_key; static void init_ncpus() { - ncpus = sysconf(_SC_NPROCESSORS_ONLN); + ncpus = sysconf(_SC_NPROCESSORS_CONF); if (ncpus < 1 || ncpus > INT32_MAX) { gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); ncpus = 1; diff --git a/src/core/lib/gpr/log_linux.cc b/src/core/lib/gpr/log_linux.cc index d743eedf38..e4417d9d5d 100644 --- a/src/core/lib/gpr/log_linux.cc +++ b/src/core/lib/gpr/log_linux.cc @@ -32,6 +32,7 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include <grpc/support/time.h> +#include <inttypes.h> #include <stdarg.h> #include <stdio.h> #include <string.h> diff --git a/src/core/lib/gpr/thd.cc b/src/core/lib/gpr/thd.cc deleted file mode 100644 index b5341c41b4..0000000000 --- a/src/core/lib/gpr/thd.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Platform-independent features for gpr threads. */ - -#include <grpc/support/port_platform.h> - -#include "src/core/lib/gpr/thd.h" - -#include <string.h> - -enum { GPR_THD_JOINABLE = 1 }; - -gpr_thd_options gpr_thd_options_default(void) { - gpr_thd_options options; - memset(&options, 0, sizeof(options)); - return options; -} - -void gpr_thd_options_set_detached(gpr_thd_options* options) { - options->flags &= ~GPR_THD_JOINABLE; -} - -void gpr_thd_options_set_joinable(gpr_thd_options* options) { - options->flags |= GPR_THD_JOINABLE; -} - -int gpr_thd_options_is_detached(const gpr_thd_options* options) { - if (!options) return 1; - return (options->flags & GPR_THD_JOINABLE) == 0; -} - -int gpr_thd_options_is_joinable(const gpr_thd_options* options) { - if (!options) return 0; - return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; -} diff --git a/src/core/lib/gpr/thd.h b/src/core/lib/gpr/thd.h deleted file mode 100644 index 920b336708..0000000000 --- a/src/core/lib/gpr/thd.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef GRPC_CORE_LIB_GPR_THD_H -#define GRPC_CORE_LIB_GPR_THD_H -/** Internal thread interface for GPR. - - Types - gpr_thd_options options used when creating a thread - */ - -#include <grpc/support/port_platform.h> - -#include <grpc/support/thd_id.h> -#include <grpc/support/time.h> - -/** Thread creation options. */ -typedef struct { - int flags; /** Opaque field. Get and set with accessors below. */ -} gpr_thd_options; - -/** Create a new thread running (*thd_body)(arg) and place its thread identifier - in *t, and return true. If there are insufficient resources, return false. - thd_name is the name of the thread for identification purposes on platforms - that support thread naming. - If options==NULL, default options are used. - The thread is immediately runnable, and exits when (*thd_body)() returns. */ -int gpr_thd_new(gpr_thd_id* t, const char* thd_name, - void (*thd_body)(void* arg), void* arg, - const gpr_thd_options* options); - -/** Return a gpr_thd_options struct with all fields set to defaults. */ -gpr_thd_options gpr_thd_options_default(void); - -/** Set the thread to become detached on startup - this is the default. */ -void gpr_thd_options_set_detached(gpr_thd_options* options); - -/** Set the thread to become joinable - mutually exclusive with detached. */ -void gpr_thd_options_set_joinable(gpr_thd_options* options); - -/** Returns non-zero if the option detached is set. */ -int gpr_thd_options_is_detached(const gpr_thd_options* options); - -/** Returns non-zero if the option joinable is set. */ -int gpr_thd_options_is_joinable(const gpr_thd_options* options); - -/** Blocks until the specified thread properly terminates. - Calling this on a detached thread has unpredictable results. */ -void gpr_thd_join(gpr_thd_id t); - -/* Internal interfaces between modules within the gpr support library. */ -void gpr_thd_init(); - -/* Wait for all outstanding threads to finish, up to deadline */ -int gpr_await_threads(gpr_timespec deadline); - -#endif /* GRPC_CORE_LIB_GPR_THD_H */ diff --git a/src/core/lib/gpr/thd_posix.cc b/src/core/lib/gpr/thd_posix.cc deleted file mode 100644 index fcd174bfba..0000000000 --- a/src/core/lib/gpr/thd_posix.cc +++ /dev/null @@ -1,154 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Posix implementation for gpr threads. */ - -#include <grpc/support/port_platform.h> - -#ifdef GPR_POSIX_SYNC - -#include "src/core/lib/gpr/thd.h" - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/sync.h> -#include <grpc/support/thd_id.h> -#include <pthread.h> -#include <stdlib.h> -#include <string.h> - -#include "src/core/lib/gpr/fork.h" -#include "src/core/lib/gpr/useful.h" - -static gpr_mu g_mu; -static gpr_cv g_cv; -static int g_thread_count; -static int g_awaiting_threads; - -struct thd_arg { - void (*body)(void* arg); /* body of a thread */ - void* arg; /* argument to a thread */ - const char* name; /* name of thread. Can be nullptr. */ -}; - -static void inc_thd_count(); -static void dec_thd_count(); - -/* Body of every thread started via gpr_thd_new. */ -static void* thread_body(void* v) { - struct thd_arg a = *static_cast<struct thd_arg*>(v); - free(v); - if (a.name != nullptr) { -#if GPR_APPLE_PTHREAD_NAME - /* Apple supports 64 characters, and will truncate if it's longer. */ - pthread_setname_np(a.name); -#elif GPR_LINUX_PTHREAD_NAME - /* Linux supports 16 characters max, and will error if it's longer. */ - char buf[16]; - size_t buf_len = GPR_ARRAY_SIZE(buf) - 1; - strncpy(buf, a.name, buf_len); - buf[buf_len] = '\0'; - pthread_setname_np(pthread_self(), buf); -#endif // GPR_APPLE_PTHREAD_NAME - } - (*a.body)(a.arg); - dec_thd_count(); - return nullptr; -} - -int gpr_thd_new(gpr_thd_id* t, const char* thd_name, - void (*thd_body)(void* arg), void* arg, - const gpr_thd_options* options) { - int thread_started; - pthread_attr_t attr; - pthread_t p; - /* don't use gpr_malloc as we may cause an infinite recursion with - * the profiling code */ - struct thd_arg* a = static_cast<struct thd_arg*>(malloc(sizeof(*a))); - GPR_ASSERT(a != nullptr); - a->body = thd_body; - a->arg = arg; - a->name = thd_name; - inc_thd_count(); - - GPR_ASSERT(pthread_attr_init(&attr) == 0); - if (gpr_thd_options_is_detached(options)) { - GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == - 0); - } else { - GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == - 0); - } - thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0); - GPR_ASSERT(pthread_attr_destroy(&attr) == 0); - if (!thread_started) { - /* don't use gpr_free, as this was allocated using malloc (see above) */ - free(a); - dec_thd_count(); - } - *t = (gpr_thd_id)p; - return thread_started; -} - -gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } - -void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, nullptr); } - -/***************************************** - * Only used when fork support is enabled - */ - -static void inc_thd_count() { - if (grpc_fork_support_enabled()) { - gpr_mu_lock(&g_mu); - g_thread_count++; - gpr_mu_unlock(&g_mu); - } -} - -static void dec_thd_count() { - if (grpc_fork_support_enabled()) { - gpr_mu_lock(&g_mu); - g_thread_count--; - if (g_awaiting_threads && g_thread_count == 0) { - gpr_cv_signal(&g_cv); - } - gpr_mu_unlock(&g_mu); - } -} - -void gpr_thd_init() { - gpr_mu_init(&g_mu); - gpr_cv_init(&g_cv); - g_thread_count = 0; - g_awaiting_threads = 0; -} - -int gpr_await_threads(gpr_timespec deadline) { - gpr_mu_lock(&g_mu); - g_awaiting_threads = 1; - int res = 0; - if (g_thread_count > 0) { - res = gpr_cv_wait(&g_cv, &g_mu, deadline); - } - g_awaiting_threads = 0; - gpr_mu_unlock(&g_mu); - return res == 0; -} - -#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/gpr/thd_windows.cc b/src/core/lib/gpr/thd_windows.cc deleted file mode 100644 index b467bd2662..0000000000 --- a/src/core/lib/gpr/thd_windows.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Windows implementation for gpr threads. */ - -#include <grpc/support/port_platform.h> - -#ifdef GPR_WINDOWS - -#include "src/core/lib/gpr/thd.h" - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/thd_id.h> -#include <string.h> - -#if defined(_MSC_VER) -#define thread_local __declspec(thread) -#elif defined(__GNUC__) -#define thread_local __thread -#else -#error "Unknown compiler - please file a bug report" -#endif - -struct thd_info { - void (*body)(void* arg); /* body of a thread */ - void* arg; /* argument to a thread */ - HANDLE join_event; /* if joinable, the join event */ - int joinable; /* true if not detached */ -}; - -static thread_local struct thd_info* g_thd_info; - -/* Destroys a thread info */ -static void destroy_thread(struct thd_info* t) { - if (t->joinable) CloseHandle(t->join_event); - gpr_free(t); -} - -void gpr_thd_init(void) {} - -/* Body of every thread started via gpr_thd_new. */ -static DWORD WINAPI thread_body(void* v) { - g_thd_info = (struct thd_info*)v; - g_thd_info->body(g_thd_info->arg); - if (g_thd_info->joinable) { - BOOL ret = SetEvent(g_thd_info->join_event); - GPR_ASSERT(ret); - } else { - destroy_thread(g_thd_info); - } - return 0; -} - -int gpr_thd_new(gpr_thd_id* t, const char* thd_name, - void (*thd_body)(void* arg), void* arg, - const gpr_thd_options* options) { - HANDLE handle; - struct thd_info* info = (struct thd_info*)gpr_malloc(sizeof(*info)); - info->body = thd_body; - info->arg = arg; - *t = 0; - if (gpr_thd_options_is_joinable(options)) { - info->joinable = 1; - info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (info->join_event == NULL) { - gpr_free(info); - return 0; - } - } else { - info->joinable = 0; - } - handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL); - if (handle == NULL) { - destroy_thread(info); - } else { - *t = (gpr_thd_id)info; - CloseHandle(handle); - } - return handle != NULL; -} - -gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } - -void gpr_thd_join(gpr_thd_id t) { - struct thd_info* info = (struct thd_info*)t; - DWORD ret = WaitForSingleObject(info->join_event, INFINITE); - GPR_ASSERT(ret == WAIT_OBJECT_0); - destroy_thread(info); -} - -#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/gprpp/manual_constructor.h b/src/core/lib/gprpp/manual_constructor.h index a177048605..7f827ca8b7 100644 --- a/src/core/lib/gprpp/manual_constructor.h +++ b/src/core/lib/gprpp/manual_constructor.h @@ -156,7 +156,7 @@ class PolymorphicManualConstructor { static_assert( manual_ctor_impl::is_one_of<DerivedType, DerivedTypes...>::value, "DerivedType must be one of the predeclared DerivedTypes"); - GPR_ASSERT(reinterpret_cast<BaseType*>(static_cast<DerivedType*>(p)) == p); + GPR_ASSERT(static_cast<BaseType*>(p) == p); } typename std::aligned_storage< diff --git a/src/core/lib/gprpp/memory.h b/src/core/lib/gprpp/memory.h index f84e20eeea..ba2f546675 100644 --- a/src/core/lib/gprpp/memory.h +++ b/src/core/lib/gprpp/memory.h @@ -30,12 +30,12 @@ namespace grpc_core { // The alignment of memory returned by gpr_malloc(). -constexpr size_t kAllignmentForDefaultAllocationInBytes = 8; +constexpr size_t kAlignmentForDefaultAllocationInBytes = 8; // Alternative to new, since we cannot use it (for fear of libstdc++) template <typename T, typename... Args> inline T* New(Args&&... args) { - void* p = alignof(T) > kAllignmentForDefaultAllocationInBytes + void* p = alignof(T) > kAlignmentForDefaultAllocationInBytes ? gpr_malloc_aligned(sizeof(T), alignof(T)) : gpr_malloc(sizeof(T)); return new (p) T(std::forward<Args>(args)...); @@ -45,7 +45,7 @@ inline T* New(Args&&... args) { template <typename T> inline void Delete(T* p) { p->~T(); - if (alignof(T) > kAllignmentForDefaultAllocationInBytes) { + if (alignof(T) > kAlignmentForDefaultAllocationInBytes) { gpr_free_aligned(p); } else { gpr_free(p); diff --git a/src/core/lib/gprpp/orphanable.h b/src/core/lib/gprpp/orphanable.h index 9e9e7f015f..a5bc8d8efc 100644 --- a/src/core/lib/gprpp/orphanable.h +++ b/src/core/lib/gprpp/orphanable.h @@ -24,6 +24,7 @@ #include <grpc/support/log.h> #include <grpc/support/sync.h> +#include <cinttypes> #include <memory> #include "src/core/lib/debug/trace.h" diff --git a/src/core/lib/gprpp/ref_counted.h b/src/core/lib/gprpp/ref_counted.h index 02b115a40e..46bfaf7fb8 100644 --- a/src/core/lib/gprpp/ref_counted.h +++ b/src/core/lib/gprpp/ref_counted.h @@ -24,6 +24,8 @@ #include <grpc/support/log.h> #include <grpc/support/sync.h> +#include <cinttypes> + #include "src/core/lib/debug/trace.h" #include "src/core/lib/gprpp/abstract.h" #include "src/core/lib/gprpp/debug_location.h" diff --git a/src/core/lib/gprpp/ref_counted_ptr.h b/src/core/lib/gprpp/ref_counted_ptr.h index 72088e76ef..388e2ec410 100644 --- a/src/core/lib/gprpp/ref_counted_ptr.h +++ b/src/core/lib/gprpp/ref_counted_ptr.h @@ -33,6 +33,7 @@ template <typename T> class RefCountedPtr { public: RefCountedPtr() {} + RefCountedPtr(std::nullptr_t) {} // If value is non-null, we take ownership of a ref to it. explicit RefCountedPtr(T* value) { value_ = value; } diff --git a/src/core/lib/gprpp/thd.h b/src/core/lib/gprpp/thd.h new file mode 100644 index 0000000000..05c7ded45f --- /dev/null +++ b/src/core/lib/gprpp/thd.h @@ -0,0 +1,135 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_THD_H +#define GRPC_CORE_LIB_GPRPP_THD_H + +/** Internal thread interface. */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd_id.h> +#include <grpc/support/time.h> + +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +namespace internal { + +/// Base class for platform-specific thread-state +class ThreadInternalsInterface { + public: + virtual ~ThreadInternalsInterface() {} + virtual void Start() GRPC_ABSTRACT; + virtual void Join() GRPC_ABSTRACT; + GRPC_ABSTRACT_BASE_CLASS +}; + +} // namespace internal + +class Thread { + public: + /// Default constructor only to allow use in structs that lack constructors + /// Does not produce a validly-constructed thread; must later + /// use placement new to construct a real thread. Does not init mu_ and cv_ + Thread() : state_(FAKE), impl_(nullptr) {} + + /// Normal constructor to create a thread with name \a thd_name, + /// which will execute a thread based on function \a thd_body + /// with argument \a arg once it is started. + /// The optional \a success argument indicates whether the thread + /// is successfully created. + Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success = nullptr); + + /// Move constructor for thread. After this is called, the other thread + /// no longer represents a living thread object + Thread(Thread&& other) : state_(other.state_), impl_(other.impl_) { + other.state_ = MOVED; + other.impl_ = nullptr; + } + + /// Move assignment operator for thread. After this is called, the other + /// thread no longer represents a living thread object. Not allowed if this + /// thread actually exists + Thread& operator=(Thread&& other) { + if (this != &other) { + // TODO(vjpai): if we can be sure that all Thread's are actually + // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here. + // However, as long as threads come in structures that are + // allocated via gpr_malloc, this will not be the case, so we cannot + // assert it for the time being. + state_ = other.state_; + impl_ = other.impl_; + other.state_ = MOVED; + other.impl_ = nullptr; + } + return *this; + } + + /// The destructor is strictly optional; either the thread never came to life + /// and the constructor itself killed it or it has already been joined and + /// the Join function kills it. The destructor shouldn't have to do anything. + ~Thread() { GPR_ASSERT(impl_ == nullptr); } + + void Start() { + if (impl_ != nullptr) { + GPR_ASSERT(state_ == ALIVE); + state_ = STARTED; + impl_->Start(); + } else { + GPR_ASSERT(state_ == FAILED); + } + }; + + void Join() { + if (impl_ != nullptr) { + impl_->Join(); + grpc_core::Delete(impl_); + state_ = DONE; + impl_ = nullptr; + } else { + GPR_ASSERT(state_ == FAILED); + } + }; + + static void Init(); + static bool AwaitAll(gpr_timespec deadline); + + private: + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + + /// The thread states are as follows: + /// FAKE -- just a dummy placeholder Thread created by the default constructor + /// ALIVE -- an actual thread of control exists associated with this thread + /// STARTED -- the thread of control has been started + /// DONE -- the thread of control has completed and been joined + /// FAILED -- the thread of control never came alive + /// MOVED -- contents were moved out and we're no longer tracking them + enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED }; + ThreadState state_; + internal::ThreadInternalsInterface* impl_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_THD_H */ diff --git a/src/core/lib/gprpp/thd_posix.cc b/src/core/lib/gprpp/thd_posix.cc new file mode 100644 index 0000000000..2f6c2edcae --- /dev/null +++ b/src/core/lib/gprpp/thd_posix.cc @@ -0,0 +1,209 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_POSIX_SYNC + +#include "src/core/lib/gprpp/thd.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd_id.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +#include "src/core/lib/gpr/fork.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +namespace { +gpr_mu g_mu; +gpr_cv g_cv; +int g_thread_count; +int g_awaiting_threads; + +class ThreadInternalsPosix; +struct thd_arg { + ThreadInternalsPosix* thread; + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + const char* name; /* name of thread. Can be nullptr. */ +}; + +class ThreadInternalsPosix + : public grpc_core::internal::ThreadInternalsInterface { + public: + ThreadInternalsPosix(const char* thd_name, void (*thd_body)(void* arg), + void* arg, bool* success) + : started_(false) { + gpr_mu_init(&mu_); + gpr_cv_init(&ready_); + pthread_attr_t attr; + /* don't use gpr_malloc as we may cause an infinite recursion with + * the profiling code */ + thd_arg* info = static_cast<thd_arg*>(malloc(sizeof(*info))); + GPR_ASSERT(info != nullptr); + info->thread = this; + info->body = thd_body; + info->arg = arg; + info->name = thd_name; + inc_thd_count(); + + GPR_ASSERT(pthread_attr_init(&attr) == 0); + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == + 0); + + *success = + (pthread_create(&pthread_id_, &attr, + [](void* v) -> void* { + thd_arg arg = *static_cast<thd_arg*>(v); + free(v); + if (arg.name != nullptr) { +#if GPR_APPLE_PTHREAD_NAME + /* Apple supports 64 characters, and will + * truncate if it's longer. */ + pthread_setname_np(arg.name); +#elif GPR_LINUX_PTHREAD_NAME + /* Linux supports 16 characters max, and will + * error if it's longer. */ + char buf[16]; + size_t buf_len = GPR_ARRAY_SIZE(buf) - 1; + strncpy(buf, arg.name, buf_len); + buf[buf_len] = '\0'; + pthread_setname_np(pthread_self(), buf); +#endif // GPR_APPLE_PTHREAD_NAME + } + + gpr_mu_lock(&arg.thread->mu_); + while (!arg.thread->started_) { + gpr_cv_wait(&arg.thread->ready_, &arg.thread->mu_, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&arg.thread->mu_); + + (*arg.body)(arg.arg); + dec_thd_count(); + return nullptr; + }, + info) == 0); + + GPR_ASSERT(pthread_attr_destroy(&attr) == 0); + + if (!success) { + /* don't use gpr_free, as this was allocated using malloc (see above) */ + free(info); + dec_thd_count(); + } + }; + + ~ThreadInternalsPosix() override { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&ready_); + } + + void Start() override { + gpr_mu_lock(&mu_); + started_ = true; + gpr_cv_signal(&ready_); + gpr_mu_unlock(&mu_); + } + + void Join() override { pthread_join(pthread_id_, nullptr); } + + private: + /***************************************** + * Only used when fork support is enabled + */ + + static void inc_thd_count() { + if (grpc_fork_support_enabled()) { + gpr_mu_lock(&g_mu); + g_thread_count++; + gpr_mu_unlock(&g_mu); + } + } + + static void dec_thd_count() { + if (grpc_fork_support_enabled()) { + gpr_mu_lock(&g_mu); + g_thread_count--; + if (g_awaiting_threads && g_thread_count == 0) { + gpr_cv_signal(&g_cv); + } + gpr_mu_unlock(&g_mu); + } + } + + gpr_mu mu_; + gpr_cv ready_; + bool started_; + pthread_t pthread_id_; +}; + +} // namespace + +Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success) { + bool outcome = false; + impl_ = + grpc_core::New<ThreadInternalsPosix>(thd_name, thd_body, arg, &outcome); + if (outcome) { + state_ = ALIVE; + } else { + state_ = FAILED; + grpc_core::Delete(impl_); + impl_ = nullptr; + } + + if (success != nullptr) { + *success = outcome; + } +} + +void Thread::Init() { + gpr_mu_init(&g_mu); + gpr_cv_init(&g_cv); + g_thread_count = 0; + g_awaiting_threads = 0; +} + +bool Thread::AwaitAll(gpr_timespec deadline) { + gpr_mu_lock(&g_mu); + g_awaiting_threads = 1; + int res = 0; + while ((g_thread_count > 0) && + (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0)) { + res = gpr_cv_wait(&g_cv, &g_mu, deadline); + } + g_awaiting_threads = 0; + gpr_mu_unlock(&g_mu); + return res == 0; +} + +} // namespace grpc_core + +// The following is in the external namespace as it is exposed as C89 API +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } + +#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/gprpp/thd_windows.cc b/src/core/lib/gprpp/thd_windows.cc new file mode 100644 index 0000000000..59ea02f3d2 --- /dev/null +++ b/src/core/lib/gprpp/thd_windows.cc @@ -0,0 +1,162 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows implementation for gpr threads. */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_WINDOWS + +#include "src/core/lib/gprpp/thd.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/thd_id.h> +#include <string.h> + +#include "src/core/lib/gprpp/memory.h" + +#if defined(_MSC_VER) +#define thread_local __declspec(thread) +#define WIN_LAMBDA +#elif defined(__GNUC__) +#define thread_local __thread +#define WIN_LAMBDA WINAPI +#else +#error "Unknown compiler - please file a bug report" +#endif + +namespace { +class ThreadInternalsWindows; +struct thd_info { + ThreadInternalsWindows* thread; + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + HANDLE join_event; /* the join event */ +}; + +thread_local struct thd_info* g_thd_info; + +class ThreadInternalsWindows + : public grpc_core::internal::ThreadInternalsInterface { + public: + ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success) + : started_(false) { + gpr_mu_init(&mu_); + gpr_cv_init(&ready_); + + HANDLE handle; + info_ = (struct thd_info*)gpr_malloc(sizeof(*info_)); + info_->thread = this; + info_->body = thd_body; + info_->arg = arg; + + info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (info_->join_event == nullptr) { + gpr_free(info_); + *success = false; + } else { + handle = CreateThread( + nullptr, 64 * 1024, + [](void* v) WIN_LAMBDA -> DWORD { + g_thd_info = static_cast<thd_info*>(v); + gpr_mu_lock(&g_thd_info->thread->mu_); + while (!g_thd_info->thread->started_) { + gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&g_thd_info->thread->mu_); + g_thd_info->body(g_thd_info->arg); + BOOL ret = SetEvent(g_thd_info->join_event); + GPR_ASSERT(ret); + return 0; + }, + info_, 0, nullptr); + if (handle == nullptr) { + destroy_thread(); + *success = false; + } else { + CloseHandle(handle); + *success = true; + } + } + } + + ~ThreadInternalsWindows() override { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&ready_); + } + + void Start() override { + gpr_mu_lock(&mu_); + started_ = true; + gpr_cv_signal(&ready_); + gpr_mu_unlock(&mu_); + } + + void Join() override { + DWORD ret = WaitForSingleObject(info_->join_event, INFINITE); + GPR_ASSERT(ret == WAIT_OBJECT_0); + destroy_thread(); + } + + private: + void destroy_thread() { + CloseHandle(info_->join_event); + gpr_free(info_); + } + + gpr_mu mu_; + gpr_cv ready_; + bool started_; + thd_info* info_; +}; + +} // namespace + +namespace grpc_core { + +void Thread::Init() {} + +bool Thread::AwaitAll(gpr_timespec deadline) { + // TODO: Consider adding this if needed + return false; +} + +Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success) { + bool outcome = false; + impl_ = grpc_core::New<ThreadInternalsWindows>(thd_body, arg, &outcome); + if (outcome) { + state_ = ALIVE; + } else { + state_ = FAILED; + grpc_core::Delete(impl_); + impl_ = nullptr; + } + + if (success != nullptr) { + *success = outcome; + } +} + +} // namespace grpc_core + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/iomgr/endpoint.cc b/src/core/lib/iomgr/endpoint.cc index e22c21e4bd..92e7930111 100644 --- a/src/core/lib/iomgr/endpoint.cc +++ b/src/core/lib/iomgr/endpoint.cc @@ -20,6 +20,8 @@ #include "src/core/lib/iomgr/endpoint.h" +grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); + void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices, grpc_closure* cb) { ep->vtable->read(ep, slices, cb); diff --git a/src/core/lib/iomgr/endpoint_pair_windows.cc b/src/core/lib/iomgr/endpoint_pair_windows.cc index 416c9d88a1..177331d681 100644 --- a/src/core/lib/iomgr/endpoint_pair_windows.cc +++ b/src/core/lib/iomgr/endpoint_pair_windows.cc @@ -22,6 +22,7 @@ #ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include <errno.h> @@ -46,19 +47,19 @@ static void create_sockets(SOCKET sv[2]) { memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_family = AF_INET; - GPR_ASSERT(bind(lst_sock, (struct sockaddr*)&addr, sizeof(addr)) != + GPR_ASSERT(bind(lst_sock, (grpc_sockaddr*)&addr, sizeof(addr)) != SOCKET_ERROR); GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR); - GPR_ASSERT(getsockname(lst_sock, (struct sockaddr*)&addr, &addr_len) != + GPR_ASSERT(getsockname(lst_sock, (grpc_sockaddr*)&addr, &addr_len) != SOCKET_ERROR); cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); GPR_ASSERT(cli_sock != INVALID_SOCKET); - GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr*)&addr, addr_len, NULL, NULL, + GPR_ASSERT(WSAConnect(cli_sock, (grpc_sockaddr*)&addr, addr_len, NULL, NULL, NULL, NULL) == 0); - svr_sock = accept(lst_sock, (struct sockaddr*)&addr, &addr_len); + svr_sock = accept(lst_sock, (grpc_sockaddr*)&addr, &addr_len); GPR_ASSERT(svr_sock != INVALID_SOCKET); closesocket(lst_sock); diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc index d3cbaf9d0a..0ef7c03056 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.cc +++ b/src/core/lib/iomgr/ev_epollex_linux.cc @@ -59,7 +59,7 @@ //#define GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP 1 #define MAX_EPOLL_EVENTS 100 -#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 16 +#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 1 grpc_core::DebugOnlyTraceFlag grpc_trace_pollable_refcount(false, "pollable_refcount"); @@ -198,7 +198,6 @@ struct grpc_pollset_worker { struct grpc_pollset { gpr_mu mu; - gpr_atm worker_count; pollable* active_pollable; bool kicked_without_poller; grpc_closure* shutdown_closure; @@ -686,7 +685,6 @@ static grpc_error* pollset_kick_all(grpc_pollset* pollset) { static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { gpr_mu_init(&pollset->mu); - gpr_atm_no_barrier_store(&pollset->worker_count, 0); pollset->active_pollable = POLLABLE_REF(g_empty_pollable, "pollset"); pollset->kicked_without_poller = false; pollset->shutdown_closure = nullptr; @@ -760,20 +758,8 @@ static grpc_error* pollable_process_events(grpc_pollset* pollset, pollable* pollable_obj, bool drain) { GPR_TIMER_SCOPE("pollable_process_events", 0); static const char* err_desc = "pollset_process_events"; - // Use a simple heuristic to determine how many fd events to process - // per loop iteration. (events/workers) - int handle_count = 1; - int worker_count = gpr_atm_no_barrier_load(&pollset->worker_count); - GPR_ASSERT(worker_count > 0); - handle_count = - (pollable_obj->event_count - pollable_obj->event_cursor) / worker_count; - if (handle_count == 0) { - handle_count = 1; - } else if (handle_count > MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) { - handle_count = MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL; - } grpc_error* error = GRPC_ERROR_NONE; - for (int i = 0; (drain || i < handle_count) && + for (int i = 0; (drain || i < MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) && pollable_obj->event_cursor != pollable_obj->event_count; i++) { int n = pollable_obj->event_cursor++; @@ -898,7 +884,6 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker, GPR_TIMER_SCOPE("begin_worker", 0); bool do_poll = (pollset->shutdown_closure == nullptr && !pollset->already_shutdown); - gpr_atm_no_barrier_fetch_add(&pollset->worker_count, 1); if (worker_hdl != nullptr) *worker_hdl = worker; worker->initialized_cv = false; worker->kicked = false; @@ -979,7 +964,6 @@ static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker, if (worker->initialized_cv) { gpr_cv_destroy(&worker->cv); } - gpr_atm_no_barrier_fetch_add(&pollset->worker_count, -1); } #ifndef NDEBUG diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc index e979ff7eb5..436537bc0b 100644 --- a/src/core/lib/iomgr/ev_poll_posix.cc +++ b/src/core/lib/iomgr/ev_poll_posix.cc @@ -38,9 +38,9 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/gpr/murmur_hash.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/tls.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/block_annotate.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/wakeup_fd_cv.h" @@ -255,8 +255,13 @@ typedef struct poll_result { } poll_result; typedef struct poll_args { + grpc_core::Thread poller_thd; gpr_cv trigger; int trigger_set; + bool harvestable; + gpr_cv harvest; + bool joinable; + gpr_cv join; struct pollfd* fds; nfds_t nfds; poll_result* result; @@ -266,15 +271,17 @@ typedef struct poll_args { // This is a 2-tiered cache, we mantain a hash table // of active poll calls, so we can wait on the result -// of that call. We also maintain a freelist of inactive -// poll threads. +// of that call. We also maintain freelists of inactive +// poll args and of dead poller threads. typedef struct poll_hash_table { poll_args* free_pollers; poll_args** active_pollers; + poll_args* dead_pollers; unsigned int size; unsigned int count; } poll_hash_table; +// TODO(kpayson64): Eliminate use of global non-POD variables poll_hash_table poll_cache; grpc_cv_fd_table g_cvfds; @@ -869,7 +876,6 @@ static grpc_error* pollset_work(grpc_pollset* pollset, grpc_pollset_worker** worker_hdl, grpc_millis deadline) { GPR_TIMER_SCOPE("pollset_work", 0); - grpc_pollset_worker worker; if (worker_hdl) *worker_hdl = &worker; grpc_error* error = GRPC_ERROR_NONE; @@ -920,7 +926,8 @@ static grpc_error* pollset_work(grpc_pollset* pollset, gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset); while (keep_polling) { keep_polling = 0; - if (!pollset->kicked_without_pollers) { + if (!pollset->kicked_without_pollers || + deadline <= grpc_core::ExecCtx::Get()->Now()) { if (!added_worker) { push_front_worker(pollset, &worker); added_worker = 1; @@ -1301,6 +1308,7 @@ static void pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { static void run_poll(void* args); static void cache_poller_locked(poll_args* args); +static void cache_harvest_locked(); static void cache_insert_locked(poll_args* args) { uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd), @@ -1363,6 +1371,10 @@ static poll_args* get_poller_locked(struct pollfd* fds, nfds_t count) { poll_args* pargs = static_cast<poll_args*>(gpr_malloc(sizeof(struct poll_args))); gpr_cv_init(&pargs->trigger); + gpr_cv_init(&pargs->harvest); + gpr_cv_init(&pargs->join); + pargs->harvestable = false; + pargs->joinable = false; pargs->fds = fds; pargs->nfds = count; pargs->next = nullptr; @@ -1370,11 +1382,9 @@ static poll_args* get_poller_locked(struct pollfd* fds, nfds_t count) { pargs->trigger_set = 0; init_result(pargs); cache_poller_locked(pargs); - gpr_thd_id t_id; - gpr_thd_options opt = gpr_thd_options_default(); gpr_ref(&g_cvfds.pollcount); - gpr_thd_options_set_detached(&opt); - GPR_ASSERT(gpr_thd_new(&t_id, "grpc_poller", &run_poll, pargs, &opt)); + pargs->poller_thd = grpc_core::Thread("grpc_poller", &run_poll, pargs); + pargs->poller_thd.Start(); return pargs; } @@ -1439,7 +1449,33 @@ static void cache_destroy_locked(poll_args* args) { poll_cache.free_pollers = args->next; } - gpr_free(args); + // Now move this args to the dead poller list for later join + if (poll_cache.dead_pollers != nullptr) { + poll_cache.dead_pollers->prev = args; + } + args->prev = nullptr; + args->next = poll_cache.dead_pollers; + poll_cache.dead_pollers = args; +} + +static void cache_harvest_locked() { + while (poll_cache.dead_pollers) { + poll_args* args = poll_cache.dead_pollers; + poll_cache.dead_pollers = poll_cache.dead_pollers->next; + // Keep the list consistent in case new dead pollers get added when we + // release the lock below to wait on joining + if (poll_cache.dead_pollers) { + poll_cache.dead_pollers->prev = nullptr; + } + args->harvestable = true; + gpr_cv_signal(&args->harvest); + while (!args->joinable) { + gpr_cv_wait(&args->join, &g_cvfds.mu, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + args->poller_thd.Join(); + gpr_free(args); + } } static void decref_poll_result(poll_result* res) { @@ -1471,6 +1507,7 @@ static void run_poll(void* args) { poll_result* result = pargs->result; int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS); gpr_mu_lock(&g_cvfds.mu); + cache_harvest_locked(); if (retval != 0) { result->completed = 1; result->retval = retval; @@ -1490,6 +1527,7 @@ static void run_poll(void* args) { deadline = gpr_time_add(deadline, thread_grace); pargs->trigger_set = 0; gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline); + cache_harvest_locked(); if (!pargs->trigger_set) { cache_destroy_locked(pargs); break; @@ -1498,10 +1536,15 @@ static void run_poll(void* args) { gpr_mu_unlock(&g_cvfds.mu); } - // We still have the lock here if (gpr_unref(&g_cvfds.pollcount)) { gpr_cv_signal(&g_cvfds.shutdown_cv); } + while (!pargs->harvestable) { + gpr_cv_wait(&pargs->harvest, &g_cvfds.mu, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + pargs->joinable = true; + gpr_cv_signal(&pargs->join); gpr_mu_unlock(&g_cvfds.mu); } @@ -1514,6 +1557,7 @@ static int cvfd_poll(struct pollfd* fds, nfds_t nfds, int timeout) { nfds_t nsockfds = 0; poll_result* result = nullptr; gpr_mu_lock(&g_cvfds.mu); + cache_harvest_locked(); pollcv = static_cast<grpc_cv_node*>(gpr_malloc(sizeof(grpc_cv_node))); pollcv->next = nullptr; gpr_cv pollcv_cv; @@ -1577,12 +1621,14 @@ static int cvfd_poll(struct pollfd* fds, nfds_t nfds, int timeout) { pargs->trigger_set = 1; gpr_cv_signal(&pargs->trigger); gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + cache_harvest_locked(); res = result->retval; errno = result->err; result->watchcount--; remove_cvn(&result->watchers, pollcv); } else if (!skip_poll) { gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + cache_harvest_locked(); } idx = 0; @@ -1639,6 +1685,7 @@ static void global_cv_fd_table_init() { for (unsigned int i = 0; i < poll_cache.size; i++) { poll_cache.active_pollers[i] = nullptr; } + poll_cache.dead_pollers = nullptr; gpr_mu_unlock(&g_cvfds.mu); } @@ -1657,6 +1704,7 @@ static void global_cv_fd_table_shutdown() { grpc_poll_function = g_cvfds.poll; gpr_free(g_cvfds.cvfds); + cache_harvest_locked(); gpr_free(poll_cache.active_pollers); gpr_mu_unlock(&g_cvfds.mu); diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc index 39ce459f1e..8b80070265 100644 --- a/src/core/lib/iomgr/ev_posix.cc +++ b/src/core/lib/iomgr/ev_posix.cc @@ -224,26 +224,26 @@ void grpc_fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { g_event_engine->fd_notify_on_write(fd, closure); } -size_t grpc_pollset_size(void) { return g_event_engine->pollset_size; } +static size_t pollset_size(void) { return g_event_engine->pollset_size; } -void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { GRPC_POLLING_API_TRACE("pollset_init(%p)", pollset); g_event_engine->pollset_init(pollset, mu); } -void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { GRPC_POLLING_API_TRACE("pollset_shutdown(%p)", pollset); g_event_engine->pollset_shutdown(pollset, closure); } -void grpc_pollset_destroy(grpc_pollset* pollset) { +static void pollset_destroy(grpc_pollset* pollset) { GRPC_POLLING_API_TRACE("pollset_destroy(%p)", pollset); g_event_engine->pollset_destroy(pollset); } -grpc_error* grpc_pollset_work(grpc_pollset* pollset, - grpc_pollset_worker** worker, - grpc_millis deadline) { +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { GRPC_POLLING_API_TRACE("pollset_work(%p, %" PRIdPTR ") begin", pollset, deadline); grpc_error* err = g_event_engine->pollset_work(pollset, worker, deadline); @@ -252,8 +252,8 @@ grpc_error* grpc_pollset_work(grpc_pollset* pollset, return err; } -grpc_error* grpc_pollset_kick(grpc_pollset* pollset, - grpc_pollset_worker* specific_worker) { +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { GRPC_POLLING_API_TRACE("pollset_kick(%p, %p)", pollset, specific_worker); return g_event_engine->pollset_kick(pollset, specific_worker); } @@ -264,43 +264,57 @@ void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd) { g_event_engine->pollset_add_fd(pollset, fd); } -grpc_pollset_set* grpc_pollset_set_create(void) { +void pollset_global_init() {} +void pollset_global_shutdown() {} + +grpc_pollset_vtable grpc_posix_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +static grpc_pollset_set* pollset_set_create(void) { grpc_pollset_set* pss = g_event_engine->pollset_set_create(); GRPC_POLLING_API_TRACE("pollset_set_create(%p)", pss); return pss; } -void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) { +static void pollset_set_destroy(grpc_pollset_set* pollset_set) { GRPC_POLLING_API_TRACE("pollset_set_destroy(%p)", pollset_set); g_event_engine->pollset_set_destroy(pollset_set); } -void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) { +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { GRPC_POLLING_API_TRACE("pollset_set_add_pollset(%p, %p)", pollset_set, pollset); g_event_engine->pollset_set_add_pollset(pollset_set, pollset); } -void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) { +static void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { GRPC_POLLING_API_TRACE("pollset_set_del_pollset(%p, %p)", pollset_set, pollset); g_event_engine->pollset_set_del_pollset(pollset_set, pollset); } -void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) { +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { GRPC_POLLING_API_TRACE("pollset_set_add_pollset_set(%p, %p)", bag, item); g_event_engine->pollset_set_add_pollset_set(bag, item); } -void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) { +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { GRPC_POLLING_API_TRACE("pollset_set_del_pollset_set(%p, %p)", bag, item); g_event_engine->pollset_set_del_pollset_set(bag, item); } +grpc_pollset_set_vtable grpc_posix_pollset_set_vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + void grpc_pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { GRPC_POLLING_API_TRACE("pollset_set_add_fd(%p, %d)", pollset_set, grpc_fd_wrapped_fd(fd)); diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc index 132fe87870..2f544b20ab 100644 --- a/src/core/lib/iomgr/exec_ctx.cc +++ b/src/core/lib/iomgr/exec_ctx.cc @@ -23,7 +23,7 @@ #include <grpc/support/log.h> #include <grpc/support/sync.h> -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/profiling/timers.h" diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h index de97164f02..72d0ae58c1 100644 --- a/src/core/lib/iomgr/exec_ctx.h +++ b/src/core/lib/iomgr/exec_ctx.h @@ -54,23 +54,32 @@ grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec timespec); namespace grpc_core { /** Execution context. * A bag of data that collects information along a callstack. - * Generally created at public API entry points, and passed down as - * pointer to child functions that manipulate it. + * It is created on the stack at public API entry points, and stored internally + * as a thread-local variable. + * + * Generally, to create an exec_ctx instance, add the following line at the top + * of the public API entry point or at the start of a thread's work function : + * + * grpc_core::ExecCtx exec_ctx; + * + * Access the created ExecCtx instance using : + * grpc_core::ExecCtx::Get() * * Specific responsibilities (this may grow in the future): * - track a list of work that needs to be delayed until the top of the * call stack (this provides a convenient mechanism to run callbacks * without worrying about locking issues) - * - provide a decision maker (via grpc_exec_ctx_ready_to_finish) that provides + * - provide a decision maker (via IsReadyToFinish) that provides a * signal as to whether a borrowed thread should continue to do work or * should actively try to finish up and get this thread back to its owner * * CONVENTIONS: * - Instance of this must ALWAYS be constructed on the stack, never * heap allocated. - * - Instances and pointers to them must always be called exec_ctx. - * - Instances are always passed as the first argument to a function that - * takes it, and always as a pointer (grpc_exec_ctx is never copied). + * - Exactly one instance of ExecCtx must be created per thread. Instances must + * always be called exec_ctx. + * - Do not pass exec_ctx as a parameter to a function. Always access it using + * grpc_core::ExecCtx::Get() */ class ExecCtx { public: @@ -171,6 +180,10 @@ on outside context */ return reinterpret_cast<ExecCtx*>(gpr_tls_get(&exec_ctx_)); } + static void Set(ExecCtx* exec_ctx) { + gpr_tls_set(&exec_ctx_, reinterpret_cast<intptr_t>(exec_ctx)); + } + protected: /** Check if ready to finish */ virtual bool CheckReadyToFinish() { return false; } @@ -180,9 +193,6 @@ on outside context */ private: /** Set exec_ctx_ to exec_ctx */ - void Set(ExecCtx* exec_ctx) { - gpr_tls_set(&exec_ctx_, reinterpret_cast<intptr_t>(exec_ctx)); - } grpc_closure_list closure_list_ = GRPC_CLOSURE_LIST_INIT; CombinerData combiner_data_ = {nullptr, nullptr}; diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc index e7f412a562..b017db53f8 100644 --- a/src/core/lib/iomgr/executor.cc +++ b/src/core/lib/iomgr/executor.cc @@ -29,9 +29,9 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/gpr/spinlock.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/tls.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/exec_ctx.h" #define MAX_DEPTH 2 @@ -43,7 +43,7 @@ typedef struct { size_t depth; bool shutdown; bool queued_long_job; - gpr_thd_id id; + grpc_core::Thread thd; } thread_state; static thread_state* g_thread_state; @@ -101,13 +101,13 @@ void grpc_executor_set_threading(bool threading) { for (size_t i = 0; i < g_max_threads; i++) { gpr_mu_init(&g_thread_state[i].mu); gpr_cv_init(&g_thread_state[i].cv); + g_thread_state[i].thd = grpc_core::Thread(); g_thread_state[i].elems = GRPC_CLOSURE_LIST_INIT; } - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - gpr_thd_new(&g_thread_state[0].id, "grpc_executor", executor_thread, - &g_thread_state[0], &opt); + g_thread_state[0].thd = + grpc_core::Thread("grpc_executor", executor_thread, &g_thread_state[0]); + g_thread_state[0].thd.Start(); } else { if (cur_threads == 0) return; for (size_t i = 0; i < g_max_threads; i++) { @@ -121,7 +121,7 @@ void grpc_executor_set_threading(bool threading) { gpr_spinlock_lock(&g_adding_thread_lock); gpr_spinlock_unlock(&g_adding_thread_lock); for (gpr_atm i = 0; i < g_cur_threads; i++) { - gpr_thd_join(g_thread_state[i].id); + g_thread_state[i].thd.Join(); } gpr_atm_no_barrier_store(&g_cur_threads, 0); for (size_t i = 0; i < g_max_threads; i++) { @@ -264,10 +264,10 @@ static void executor_push(grpc_closure* closure, grpc_error* error, if (cur_thread_count < g_max_threads) { gpr_atm_no_barrier_store(&g_cur_threads, cur_thread_count + 1); - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - gpr_thd_new(&g_thread_state[cur_thread_count].id, "gpr_executor", - executor_thread, &g_thread_state[cur_thread_count], &opt); + g_thread_state[cur_thread_count].thd = + grpc_core::Thread("grpc_executor", executor_thread, + &g_thread_state[cur_thread_count]); + g_thread_state[cur_thread_count].thd.Start(); } gpr_spinlock_unlock(&g_adding_thread_lock); } diff --git a/src/core/lib/iomgr/fork_posix.cc b/src/core/lib/iomgr/fork_posix.cc index d32fbc4588..f8645ab157 100644 --- a/src/core/lib/iomgr/fork_posix.cc +++ b/src/core/lib/iomgr/fork_posix.cc @@ -29,7 +29,7 @@ #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/fork.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/timer_manager.h" @@ -53,7 +53,7 @@ void grpc_prefork() { grpc_timer_manager_set_threading(false); grpc_executor_set_threading(false); grpc_core::ExecCtx::Get()->Flush(); - if (!gpr_await_threads( + if (!grpc_core::Thread::AwaitAll( gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(3, GPR_TIMESPAN)))) { gpr_log(GPR_ERROR, "gRPC thread still active! Cannot fork!"); diff --git a/src/core/lib/iomgr/iocp_windows.cc b/src/core/lib/iomgr/iocp_windows.cc index 5285734719..ce77231036 100644 --- a/src/core/lib/iomgr/iocp_windows.cc +++ b/src/core/lib/iomgr/iocp_windows.cc @@ -30,7 +30,7 @@ #include <grpc/support/log_windows.h> #include "src/core/lib/debug/stats.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/socket_windows.h" diff --git a/src/core/lib/iomgr/iomgr.cc b/src/core/lib/iomgr/iomgr.cc index 70a80e1998..468814eaee 100644 --- a/src/core/lib/iomgr/iomgr.cc +++ b/src/core/lib/iomgr/iomgr.cc @@ -31,8 +31,8 @@ #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" @@ -47,6 +47,7 @@ static grpc_iomgr_object g_root_object; void grpc_iomgr_init() { grpc_core::ExecCtx exec_ctx; + grpc_determine_iomgr_platform(); g_shutdown = 0; gpr_mu_init(&g_mu); gpr_cv_init(&g_rcv); diff --git a/src/core/lib/iomgr/iomgr_custom.cc b/src/core/lib/iomgr/iomgr_custom.cc new file mode 100644 index 0000000000..d34c8e7cd1 --- /dev/null +++ b/src/core/lib/iomgr/iomgr_custom.cc @@ -0,0 +1,63 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <grpc/support/thd_id.h> + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/pollset_set_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" + +gpr_thd_id g_init_thread; + +static void iomgr_platform_init(void) { + grpc_core::ExecCtx exec_ctx; + grpc_executor_set_threading(false); + g_init_thread = gpr_thd_currentid(); + grpc_pollset_global_init(); +} +static void iomgr_platform_flush(void) {} +static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } + +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown}; + +void grpc_custom_iomgr_init(grpc_socket_vtable* socket, + grpc_custom_resolver_vtable* resolver, + grpc_custom_timer_vtable* timer, + grpc_custom_poller_vtable* poller) { + grpc_custom_endpoint_init(socket); + grpc_custom_timer_init(timer); + grpc_custom_pollset_init(poller); + grpc_custom_pollset_set_init(); + grpc_custom_resolver_init(resolver); + grpc_set_iomgr_platform_vtable(&vtable); +} + +#ifdef GRPC_CUSTOM_SOCKET +grpc_iomgr_platform_vtable* grpc_default_iomgr_platform_vtable() { + return &vtable; +} +#endif diff --git a/src/core/lib/iomgr/iomgr_uv.h b/src/core/lib/iomgr/iomgr_custom.h index 4d62f00ad6..ceb6c65db2 100644 --- a/src/core/lib/iomgr/iomgr_uv.h +++ b/src/core/lib/iomgr/iomgr_custom.h @@ -16,24 +16,32 @@ * */ -#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_UV_H -#define GRPC_CORE_LIB_IOMGR_IOMGR_UV_H +#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H #include <grpc/support/port_platform.h> -#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer_custom.h" #include <grpc/support/thd_id.h> /* The thread ID of the thread on which grpc was initialized. Used to verify - * that all calls into libuv are made on that same thread */ + * that all calls into the custom iomgr are made on that same thread */ extern gpr_thd_id g_init_thread; -#ifdef GRPC_UV_THREAD_CHECK -#define GRPC_UV_ASSERT_SAME_THREAD() \ +#ifdef GRPC_CUSTOM_IOMGR_THREAD_CHECK +#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() \ GPR_ASSERT(gpr_thd_currentid() == g_init_thread) #else -#define GRPC_UV_ASSERT_SAME_THREAD() +#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() #endif /* GRPC_UV_THREAD_CHECK */ -#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_UV_H */ +void grpc_custom_iomgr_init(grpc_socket_vtable* socket, + grpc_custom_resolver_vtable* resolver, + grpc_custom_timer_vtable* timer, + grpc_custom_poller_vtable* poller); + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H */ diff --git a/src/core/lib/iomgr/iomgr_internal.cc b/src/core/lib/iomgr/iomgr_internal.cc new file mode 100644 index 0000000000..32dbabb79d --- /dev/null +++ b/src/core/lib/iomgr/iomgr_internal.cc @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stddef.h> + +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +static grpc_iomgr_platform_vtable* iomgr_platform_vtable = nullptr; + +void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable) { + iomgr_platform_vtable = vtable; +} + +void grpc_determine_iomgr_platform() { + if (iomgr_platform_vtable == nullptr) { + grpc_set_default_iomgr_platform(); + } +} + +void grpc_iomgr_platform_init() { iomgr_platform_vtable->init(); } + +void grpc_iomgr_platform_flush() { iomgr_platform_vtable->flush(); } + +void grpc_iomgr_platform_shutdown() { iomgr_platform_vtable->shutdown(); } diff --git a/src/core/lib/iomgr/iomgr_internal.h b/src/core/lib/iomgr/iomgr_internal.h index 644219fb4d..b011d9c7b1 100644 --- a/src/core/lib/iomgr/iomgr_internal.h +++ b/src/core/lib/iomgr/iomgr_internal.h @@ -31,9 +31,21 @@ typedef struct grpc_iomgr_object { struct grpc_iomgr_object* prev; } grpc_iomgr_object; +typedef struct grpc_iomgr_platform_vtable { + void (*init)(void); + void (*flush)(void); + void (*shutdown)(void); +} grpc_iomgr_platform_vtable; + void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name); void grpc_iomgr_unregister_object(grpc_iomgr_object* obj); +void grpc_determine_iomgr_platform(); + +void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable); + +void grpc_set_default_iomgr_platform(); + void grpc_iomgr_platform_init(void); /** flush any globally queued work from iomgr */ void grpc_iomgr_platform_flush(void); diff --git a/src/core/lib/iomgr/iomgr_posix.cc b/src/core/lib/iomgr/iomgr_posix.cc index 35b8adf01e..66c9cb7ff7 100644 --- a/src/core/lib/iomgr/iomgr_posix.cc +++ b/src/core/lib/iomgr/iomgr_posix.cc @@ -24,19 +24,44 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_client.h" #include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" -void grpc_iomgr_platform_init(void) { +extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_posix_pollset_vtable; +extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_posix_resolver_vtable; + +static void iomgr_platform_init(void) { grpc_wakeup_fd_global_init(); grpc_event_engine_init(); } -void grpc_iomgr_platform_flush(void) {} +static void iomgr_platform_flush(void) {} -void grpc_iomgr_platform_shutdown(void) { +static void iomgr_platform_shutdown(void) { grpc_event_engine_shutdown(); grpc_wakeup_fd_global_destroy(); } +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown}; + +void grpc_set_default_iomgr_platform() { + grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable); + grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_posix_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_posix_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + #endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr_uv.cc b/src/core/lib/iomgr/iomgr_uv.cc index c11c37ca20..4a984446db 100644 --- a/src/core/lib/iomgr/iomgr_uv.cc +++ b/src/core/lib/iomgr/iomgr_uv.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2016 gRPC authors. + * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,26 +20,21 @@ #include "src/core/lib/iomgr/port.h" -#ifdef GRPC_UV +#if defined(GRPC_CUSTOM_SOCKET) && defined(GRPC_UV) -#include <grpc/support/thd_id.h> +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer_custom.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/pollset_uv.h" -#include "src/core/lib/iomgr/tcp_uv.h" +extern grpc_socket_vtable grpc_uv_socket_vtable; +extern grpc_custom_resolver_vtable uv_resolver_vtable; +extern grpc_custom_timer_vtable uv_timer_vtable; +extern grpc_custom_poller_vtable uv_pollset_vtable; -gpr_thd_id g_init_thread; - -void grpc_iomgr_platform_init(void) { - grpc_core::ExecCtx exec_ctx; - grpc_pollset_global_init(); - - grpc_executor_set_threading(false); - g_init_thread = gpr_thd_currentid(); +void grpc_set_default_iomgr_platform() { + grpc_custom_iomgr_init(&grpc_uv_socket_vtable, &uv_resolver_vtable, + &uv_timer_vtable, &uv_pollset_vtable); } -void grpc_iomgr_platform_flush(void) {} -void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } - -#endif /* GRPC_UV */ +#endif diff --git a/src/core/lib/iomgr/iomgr_windows.cc b/src/core/lib/iomgr/iomgr_windows.cc index 8c4888ca97..cdef89cbf0 100644 --- a/src/core/lib/iomgr/iomgr_windows.cc +++ b/src/core/lib/iomgr/iomgr_windows.cc @@ -29,7 +29,18 @@ #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_tcp_server_vtable grpc_windows_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_windows_tcp_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_windows_pollset_vtable; +extern grpc_pollset_set_vtable grpc_windows_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_windows_resolver_vtable; /* Windows' io manager is going to be fully designed using IO completion ports. All of what we're doing here is basically make sure that @@ -46,18 +57,31 @@ static void winsock_shutdown(void) { GPR_ASSERT(status == 0); } -void grpc_iomgr_platform_init(void) { +static void iomgr_platform_init(void) { winsock_init(); grpc_iocp_init(); grpc_pollset_global_init(); } -void grpc_iomgr_platform_flush(void) { grpc_iocp_flush(); } +static void iomgr_platform_flush(void) { grpc_iocp_flush(); } -void grpc_iomgr_platform_shutdown(void) { +static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); grpc_iocp_shutdown(); winsock_shutdown(); } +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown}; + +void grpc_set_default_iomgr_platform() { + grpc_set_tcp_client_impl(&grpc_windows_tcp_client_vtable); + grpc_set_tcp_server_impl(&grpc_windows_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_windows_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_windows_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_windows_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset.cc b/src/core/lib/iomgr/pollset.cc new file mode 100644 index 0000000000..ebfef1dbc7 --- /dev/null +++ b/src/core/lib/iomgr/pollset.cc @@ -0,0 +1,56 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/pollset.h" + +grpc_pollset_vtable* grpc_pollset_impl; + +void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable) { + grpc_pollset_impl = vtable; +} + +void grpc_pollset_global_init() { grpc_pollset_impl->global_init(); } + +void grpc_pollset_global_shutdown() { grpc_pollset_impl->global_shutdown(); } + +void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + grpc_pollset_impl->init(pollset, mu); +} + +void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + grpc_pollset_impl->shutdown(pollset, closure); +} + +void grpc_pollset_destroy(grpc_pollset* pollset) { + grpc_pollset_impl->destroy(pollset); +} + +grpc_error* grpc_pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { + return grpc_pollset_impl->work(pollset, worker, deadline); +} + +grpc_error* grpc_pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + return grpc_pollset_impl->kick(pollset, specific_worker); +} + +size_t grpc_pollset_size(void) { return grpc_pollset_impl->pollset_size(); } diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h index 9cc3e4c7fa..28472b360d 100644 --- a/src/core/lib/iomgr/pollset.h +++ b/src/core/lib/iomgr/pollset.h @@ -38,6 +38,24 @@ extern grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount; typedef struct grpc_pollset grpc_pollset; typedef struct grpc_pollset_worker grpc_pollset_worker; +typedef struct grpc_pollset_vtable { + void (*global_init)(void); + void (*global_shutdown)(void); + void (*init)(grpc_pollset* pollset, gpr_mu** mu); + void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure); + void (*destroy)(grpc_pollset* pollset); + grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker, + grpc_millis deadline); + grpc_error* (*kick)(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker); + size_t (*pollset_size)(void); +} grpc_pollset_vtable; + +void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable); + +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); + size_t grpc_pollset_size(void); /* Initialize a pollset: assumes *pollset contains all zeros */ void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu); diff --git a/src/core/lib/iomgr/pollset_custom.cc b/src/core/lib/iomgr/pollset_custom.cc new file mode 100644 index 0000000000..04bd104055 --- /dev/null +++ b/src/core/lib/iomgr/pollset_custom.cc @@ -0,0 +1,106 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <stddef.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/timer.h" + +#include "src/core/lib/debug/trace.h" + +static grpc_custom_poller_vtable* poller_vtable; + +struct grpc_pollset { + gpr_mu mu; +}; + +static size_t pollset_size() { return sizeof(grpc_pollset); } + +static void pollset_global_init() { poller_vtable->init(); } + +static void pollset_global_shutdown() { poller_vtable->shutdown(); } + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); +} + +static void pollset_destroy(grpc_pollset* pollset) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_destroy(&pollset->mu); +} + +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_unlock(&pollset->mu); + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + size_t timeout = 0; + if (deadline > now) { + timeout = deadline - now; + } + // We yield here because the poll() call might yield + // control back to the application + grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get(); + grpc_core::ExecCtx::Set(nullptr); + poller_vtable->poll(timeout); + grpc_core::ExecCtx::Set(curr); + grpc_core::ExecCtx::Get()->InvalidateNow(); + if (grpc_core::ExecCtx::Get()->HasWork()) { + grpc_core::ExecCtx::Get()->Flush(); + } + gpr_mu_lock(&pollset->mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + poller_vtable->kick(); + return GRPC_ERROR_NONE; +} + +grpc_pollset_vtable custom_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable) { + poller_vtable = vtable; + grpc_set_pollset_vtable(&custom_pollset_vtable); +} diff --git a/src/core/lib/iomgr/pollset_custom.h b/src/core/lib/iomgr/pollset_custom.h new file mode 100644 index 0000000000..9e2027f7f4 --- /dev/null +++ b/src/core/lib/iomgr/pollset_custom.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include <stddef.h> + +typedef struct grpc_custom_poller_vtable { + void (*init)(); + void (*poll)(size_t timeout_ms); + void (*kick)(); + void (*shutdown)(); +} grpc_custom_poller_vtable; + +void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H */ diff --git a/src/core/lib/iomgr/pollset_set_uv.cc b/src/core/lib/iomgr/pollset_set.cc index 50814c1f0a..42a647a737 100644 --- a/src/core/lib/iomgr/pollset_set_uv.cc +++ b/src/core/lib/iomgr/pollset_set.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2016 gRPC authors. + * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,28 +18,38 @@ #include <grpc/support/port_platform.h> -#include "src/core/lib/iomgr/port.h" +#include "src/core/lib/iomgr/pollset_set.h" -#ifdef GRPC_UV +grpc_pollset_set_vtable* grpc_pollset_set_impl; -#include "src/core/lib/iomgr/pollset_set.h" +void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable) { + grpc_pollset_set_impl = vtable; +} -grpc_pollset_set* grpc_pollset_set_create(void) { - return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +grpc_pollset_set* grpc_pollset_set_create() { + return grpc_pollset_set_impl->create(); } -void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {} +void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) { + grpc_pollset_set_impl->destroy(pollset_set); +} void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} + grpc_pollset* pollset) { + grpc_pollset_set_impl->add_pollset(pollset_set, pollset); +} void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} + grpc_pollset* pollset) { + grpc_pollset_set_impl->del_pollset(pollset_set, pollset); +} void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} + grpc_pollset_set* item) { + grpc_pollset_set_impl->add_pollset_set(bag, item); +} void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -#endif /* GRPC_UV */ + grpc_pollset_set* item) { + grpc_pollset_set_impl->del_pollset_set(bag, item); +} diff --git a/src/core/lib/iomgr/pollset_set.h b/src/core/lib/iomgr/pollset_set.h index 18f30aa94e..d3355b8ff8 100644 --- a/src/core/lib/iomgr/pollset_set.h +++ b/src/core/lib/iomgr/pollset_set.h @@ -30,6 +30,17 @@ typedef struct grpc_pollset_set grpc_pollset_set; +typedef struct grpc_pollset_set_vtable { + grpc_pollset_set* (*create)(void); + void (*destroy)(grpc_pollset_set* pollset_set); + void (*add_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset); + void (*del_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset); + void (*add_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item); + void (*del_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item); +} grpc_pollset_set_vtable; + +void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable); + grpc_pollset_set* grpc_pollset_set_create(void); void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set); void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, diff --git a/src/core/lib/iomgr/pollset_set_custom.cc b/src/core/lib/iomgr/pollset_set_custom.cc new file mode 100644 index 0000000000..b1ee66020d --- /dev/null +++ b/src/core/lib/iomgr/pollset_set_custom.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set* pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +void pollset_set_destroy(grpc_pollset_set* pollset_set) {} + +void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +static grpc_pollset_set_vtable vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + +void grpc_custom_pollset_set_init() { grpc_set_pollset_set_vtable(&vtable); } diff --git a/src/core/lib/iomgr/pollset_set_custom.h b/src/core/lib/iomgr/pollset_set_custom.h new file mode 100644 index 0000000000..80e19a1fef --- /dev/null +++ b/src/core/lib/iomgr/pollset_set_custom.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H + +#include <grpc/support/port_platform.h> + +void grpc_custom_pollset_set_init(); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H */ diff --git a/src/core/lib/iomgr/pollset_set_windows.cc b/src/core/lib/iomgr/pollset_set_windows.cc index ff3f6a944e..bb9e7f5d28 100644 --- a/src/core/lib/iomgr/pollset_set_windows.cc +++ b/src/core/lib/iomgr/pollset_set_windows.cc @@ -25,22 +25,27 @@ #include "src/core/lib/iomgr/pollset_set_windows.h" -grpc_pollset_set* grpc_pollset_set_create(void) { +static grpc_pollset_set* pollset_set_create(void) { return (grpc_pollset_set*)((intptr_t)0xdeafbeef); } -void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {} +static void pollset_set_destroy(grpc_pollset_set* pollset_set) {} -void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} -void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} +static void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} -void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} -void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +grpc_pollset_set_vtable grpc_windows_pollset_set_vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_uv.cc b/src/core/lib/iomgr/pollset_uv.cc index c6a2f43bf1..bade6eae6c 100644 --- a/src/core/lib/iomgr/pollset_uv.cc +++ b/src/core/lib/iomgr/pollset_uv.cc @@ -22,137 +22,72 @@ #ifdef GRPC_UV -#include <uv.h> - -#include <string.h> - #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/sync.h> - -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/pollset.h" -#include "src/core/lib/iomgr/pollset_uv.h" +#include "src/core/lib/iomgr/pollset_custom.h" -#include "src/core/lib/debug/trace.h" - -grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount"); - -struct grpc_pollset { - uv_timer_t* timer; - int shutting_down; -}; +#include <uv.h> /* Indicates that grpc_pollset_work should run an iteration of the UV loop before running callbacks. This defaults to 1, and should be disabled if grpc_pollset_work will be called within the callstack of uv_run */ -int grpc_pollset_work_run_loop; - -gpr_mu grpc_polling_mu; +int grpc_pollset_work_run_loop = 1; -/* This is used solely to kick the uv loop, by setting a callback to be run - immediately in the next loop iteration. - Note: In the future, if there is a bug that involves missing wakeups in the - future, try adding a uv_async_t to kick the loop differently */ -uv_timer_t* dummy_uv_handle; +static bool g_kicked = false; -size_t grpc_pollset_size() { return sizeof(grpc_pollset); } +typedef struct uv_poller_handle { + uv_timer_t poll_timer; + uv_timer_t kick_timer; + int refs; +} uv_poller_handle; -void dummy_timer_cb(uv_timer_t* handle) {} +static uv_poller_handle* g_handle; -void dummy_handle_close_cb(uv_handle_t* handle) { gpr_free(handle); } - -void grpc_pollset_global_init(void) { - gpr_mu_init(&grpc_polling_mu); - dummy_uv_handle = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); - uv_timer_init(uv_default_loop(), dummy_uv_handle); - grpc_pollset_work_run_loop = 1; -} - -void grpc_pollset_global_shutdown(void) { - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_mu_destroy(&grpc_polling_mu); - uv_close((uv_handle_t*)dummy_uv_handle, dummy_handle_close_cb); +static void init() { + g_handle = (uv_poller_handle*)gpr_malloc(sizeof(uv_poller_handle)); + g_handle->refs = 2; + uv_timer_init(uv_default_loop(), &g_handle->poll_timer); + uv_timer_init(uv_default_loop(), &g_handle->kick_timer); } -static void timer_run_cb(uv_timer_t* timer) {} +static void empty_timer_cb(uv_timer_t* handle) {} -static void timer_close_cb(uv_handle_t* handle) { - handle->data = (void*)1; - gpr_free(handle); -} +static void kick_timer_cb(uv_timer_t* handle) { g_kicked = false; } -void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { - GRPC_UV_ASSERT_SAME_THREAD(); - *mu = &grpc_polling_mu; - pollset->timer = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); - uv_timer_init(uv_default_loop(), pollset->timer); - pollset->shutting_down = 0; +static void run_loop(size_t timeout) { + if (grpc_pollset_work_run_loop) { + if (timeout == 0) { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } else { + uv_timer_start(&g_handle->poll_timer, empty_timer_cb, timeout, 0); + uv_run(uv_default_loop(), UV_RUN_ONCE); + uv_timer_stop(&g_handle->poll_timer); + } + } } -void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { - GPR_ASSERT(!pollset->shutting_down); - GRPC_UV_ASSERT_SAME_THREAD(); - pollset->shutting_down = 1; - if (grpc_pollset_work_run_loop) { - // Drain any pending UV callbacks without blocking - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } else { - // kick the loop once - uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); +static void kick() { + if (!g_kicked) { + g_kicked = true; + uv_timer_start(&g_handle->kick_timer, kick_timer_cb, 0, 0); } - GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); } -void grpc_pollset_destroy(grpc_pollset* pollset) { - GRPC_UV_ASSERT_SAME_THREAD(); - uv_close((uv_handle_t*)pollset->timer, timer_close_cb); - // timer.data is a boolean indicating that the timer has finished closing - pollset->timer->data = (void*)0; - if (grpc_pollset_work_run_loop) { - while (!pollset->timer->data) { - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } +static void close_timer_cb(uv_handle_t* handle) { + g_handle->refs--; + if (g_handle->refs == 0) { + gpr_free(g_handle); } } -grpc_error* grpc_pollset_work(grpc_pollset* pollset, - grpc_pollset_worker** worker_hdl, - grpc_millis deadline) { - uint64_t timeout; - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_mu_unlock(&grpc_polling_mu); +static void shutdown() { + uv_close((uv_handle_t*)&g_handle->poll_timer, close_timer_cb); + uv_close((uv_handle_t*)&g_handle->kick_timer, close_timer_cb); if (grpc_pollset_work_run_loop) { - grpc_millis now = grpc_core::ExecCtx::Get()->Now(); - if (deadline >= now) { - timeout = deadline - now; - } else { - timeout = 0; - } - /* We special-case timeout=0 so that we don't bother with the timer when - the loop won't block anyway */ - if (timeout > 0) { - uv_timer_start(pollset->timer, timer_run_cb, timeout, 0); - /* Run until there is some I/O activity or the timer triggers. It doesn't - matter which happens */ - uv_run(uv_default_loop(), UV_RUN_ONCE); - uv_timer_stop(pollset->timer); - } else { - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } - } - if (!grpc_closure_list_empty(*grpc_core::ExecCtx::Get()->closure_list())) { - grpc_core::ExecCtx::Get()->Flush(); + GPR_ASSERT(uv_run(uv_default_loop(), UV_RUN_DEFAULT) == 0); } - gpr_mu_lock(&grpc_polling_mu); - return GRPC_ERROR_NONE; } -grpc_error* grpc_pollset_kick(grpc_pollset* pollset, - grpc_pollset_worker* specific_worker) { - GRPC_UV_ASSERT_SAME_THREAD(); - uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); - return GRPC_ERROR_NONE; -} +grpc_custom_poller_vtable uv_pollset_vtable = {init, run_loop, kick, shutdown}; #endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_uv.h b/src/core/lib/iomgr/pollset_uv.h index 566c110ca6..de82bcc1d3 100644 --- a/src/core/lib/iomgr/pollset_uv.h +++ b/src/core/lib/iomgr/pollset_uv.h @@ -21,7 +21,12 @@ extern int grpc_pollset_work_run_loop; -void grpc_pollset_global_init(void); -void grpc_pollset_global_shutdown(void); +typedef struct grpc_custom_poller_vtable { + void (*init)(void); + void (*run_loop)(int blocking); +} grpc_custom_poller_vtable; + +void grpc_custom_pollset_global_init(grpc_custom_poller_vtable* vtable); +void grpc_custom_pollset_global_shutdown(void); #endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */ diff --git a/src/core/lib/iomgr/pollset_windows.cc b/src/core/lib/iomgr/pollset_windows.cc index 62ab760875..e9a808d8ad 100644 --- a/src/core/lib/iomgr/pollset_windows.cc +++ b/src/core/lib/iomgr/pollset_windows.cc @@ -24,7 +24,7 @@ #include <grpc/support/log.h> -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/pollset.h" @@ -38,7 +38,7 @@ gpr_mu grpc_polling_mu; static grpc_pollset_worker* g_active_poller; static grpc_pollset_worker g_global_root_worker; -void grpc_pollset_global_init(void) { +static void pollset_global_init(void) { gpr_mu_init(&grpc_polling_mu); g_active_poller = NULL; g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = @@ -46,7 +46,7 @@ void grpc_pollset_global_init(void) { &g_global_root_worker; } -void grpc_pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } +static void pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } static void remove_worker(grpc_pollset_worker* worker, grpc_pollset_worker_link_type type) { @@ -80,21 +80,21 @@ static void push_front_worker(grpc_pollset_worker* root, worker->links[type].next->links[type].prev = worker; } -size_t grpc_pollset_size(void) { return sizeof(grpc_pollset); } +static size_t pollset_size(void) { return sizeof(grpc_pollset); } /* There isn't really any such thing as a pollset under Windows, due to the nature of the IO completion ports. We're still going to provide a minimal set of features for the sake of the rest of grpc. But grpc_pollset_work won't actually do any polling, and return as quickly as possible. */ -void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { *mu = &grpc_polling_mu; pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = &pollset->root_worker; } -void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { pollset->shutting_down = 1; grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); if (!pollset->is_iocp_worker) { @@ -104,11 +104,11 @@ void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { } } -void grpc_pollset_destroy(grpc_pollset* pollset) {} +static void pollset_destroy(grpc_pollset* pollset) {} -grpc_error* grpc_pollset_work(grpc_pollset* pollset, - grpc_pollset_worker** worker_hdl, - grpc_millis deadline) { +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { grpc_pollset_worker worker; if (worker_hdl) *worker_hdl = &worker; @@ -182,8 +182,8 @@ done: return GRPC_ERROR_NONE; } -grpc_error* grpc_pollset_kick(grpc_pollset* p, - grpc_pollset_worker* specific_worker) { +static grpc_error* pollset_kick(grpc_pollset* p, + grpc_pollset_worker* specific_worker) { if (specific_worker != NULL) { if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { for (specific_worker = @@ -220,4 +220,10 @@ grpc_error* grpc_pollset_kick(grpc_pollset* p, return GRPC_ERROR_NONE; } +grpc_pollset_vtable grpc_windows_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h index 25090898ed..c1dcc52618 100644 --- a/src/core/lib/iomgr/port.h +++ b/src/core/lib/iomgr/port.h @@ -21,8 +21,11 @@ #ifndef GRPC_CORE_LIB_IOMGR_PORT_H #define GRPC_CORE_LIB_IOMGR_PORT_H -#if defined(GRPC_UV) -// Do nothing +#ifdef GRPC_UV +#define GRPC_CUSTOM_SOCKET +#endif +#if defined(GRPC_CUSTOM_SOCKET) +// Do Nothing #elif defined(GPR_MANYLINUX1) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_HAVE_IFADDRS 1 @@ -33,13 +36,10 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #define GRPC_LINUX_EPOLL 1 #elif defined(GPR_WINDOWS) -#define GRPC_TIMER_USE_GENERIC 1 #define GRPC_WINSOCK_SOCKET 1 #define GRPC_WINDOWS_SOCKETUTILS 1 #elif defined(GPR_ANDROID) @@ -49,10 +49,8 @@ #define GRPC_HAVE_UNIX_SOCKET 1 #define GRPC_LINUX_EVENTFD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_LINUX) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_HAVE_IFADDRS 1 @@ -64,9 +62,7 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_HOST_NAME_MAX 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #ifdef __GLIBC_PREREQ #if __GLIBC_PREREQ(2, 4) #define GRPC_LINUX_EPOLL 1 @@ -100,11 +96,9 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_SYSCONF 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_FREEBSD) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_HAVE_IFADDRS 1 @@ -114,36 +108,31 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_OPENBSD) #define GRPC_HAVE_IFADDRS 1 #define GRPC_HAVE_IPV6_RECVPKTINFO 1 #define GRPC_HAVE_UNIX_SOCKET 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_NACL) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif !defined(GPR_NO_AUTODETECT_PLATFORM) #error "Platform not recognized" #endif #if defined(GRPC_POSIX_SOCKET) + defined(GRPC_WINSOCK_SOCKET) + \ - defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_UV) != \ + defined(GRPC_CUSTOM_SOCKET) != \ 1 -#error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET +#error \ + "Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GRPC_CUSTOM_SOCKET" #endif #if defined(GRPC_POSIX_HOST_NAME_MAX) && defined(GRPC_POSIX_SYSCONF) diff --git a/src/core/lib/iomgr/resolve_address.cc b/src/core/lib/iomgr/resolve_address.cc new file mode 100644 index 0000000000..f2a4676369 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/alloc.h> +#include "src/core/lib/iomgr/resolve_address.h" + +grpc_address_resolver_vtable* grpc_resolve_address_impl; + +void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable) { + grpc_resolve_address_impl = vtable; +} + +void grpc_resolve_address(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses) { + grpc_resolve_address_impl->resolve_address( + addr, default_port, interested_parties, on_done, addresses); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { + if (addrs != nullptr) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +grpc_error* grpc_blocking_resolve_address(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses) { + return grpc_resolve_address_impl->blocking_resolve_address(name, default_port, + addresses); +} diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h index 10a7822654..7da5caaa8e 100644 --- a/src/core/lib/iomgr/resolve_address.h +++ b/src/core/lib/iomgr/resolve_address.h @@ -37,20 +37,33 @@ typedef struct { grpc_resolved_address* addrs; } grpc_resolved_addresses; +typedef struct grpc_address_resolver_vtable { + void (*resolve_address)(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + grpc_error* (*blocking_resolve_address)(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses); +} grpc_address_resolver_vtable; + +void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable); + /* Asynchronously resolve addr. Use default_port if a port isn't designated in addr, otherwise use the port in addr. */ /* TODO(ctiller): add a timeout here */ -extern void (*grpc_resolve_address)(const char* addr, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addresses); +void grpc_resolve_address(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + /* Destroy resolved addresses */ void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addresses); -/* Resolve addr in a blocking fashion. Returns NULL on failure. On success, +/* Resolve addr in a blocking fashion. On success, result must be freed with grpc_resolved_addresses_destroy. */ -extern grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses); +grpc_error* grpc_blocking_resolve_address(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses); #endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */ diff --git a/src/core/lib/iomgr/resolve_address_custom.cc b/src/core/lib/iomgr/resolve_address_custom.cc new file mode 100644 index 0000000000..9cf7817f66 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_custom.cc @@ -0,0 +1,187 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/string_util.h> + +#include <grpc/support/log.h> +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" + +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include <string.h> + +typedef struct grpc_custom_resolver { + grpc_closure* on_done; + grpc_resolved_addresses** addresses; + char* host; + char* port; +} grpc_custom_resolver; + +static grpc_custom_resolver_vtable* resolve_address_vtable = nullptr; + +static int retry_named_port_failure(grpc_custom_resolver* r, + grpc_resolved_addresses** res) { + // This loop is copied from resolve_address_posix.c + const char* svc[][2] = {{"http", "80"}, {"https", "443"}}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { + if (strcmp(r->port, svc[i][0]) == 0) { + gpr_free(r->port); + r->port = gpr_strdup(svc[i][1]); + if (res) { + grpc_error* error = + resolve_address_vtable->resolve(r->host, r->port, res); + if (error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(error); + return 0; + } + } else { + resolve_address_vtable->resolve_async(r, r->host, r->port); + } + return 1; + } + } + return 0; +} + +void grpc_custom_resolve_callback(grpc_custom_resolver* r, + grpc_resolved_addresses* result, + grpc_error* error) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_core::ExecCtx exec_ctx; + if (error == GRPC_ERROR_NONE) { + *r->addresses = result; + } else if (retry_named_port_failure(r, nullptr)) { + return; + } + if (r->on_done) { + GRPC_CLOSURE_SCHED(r->on_done, error); + } + gpr_free(r->host); + gpr_free(r->port); + gpr_free(r); +} + +static grpc_error* try_split_host_port(const char* name, + const char* default_port, char** host, + char** port) { + /* parse name, splitting it into host and port parts */ + grpc_error* error; + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + char* msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + if (*port == nullptr) { + // TODO(murgatroid99): add tests for this case + if (default_port == nullptr) { + char* msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *port = gpr_strdup(default_port); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* blocking_resolve_address_impl( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + char* host; + char* port; + grpc_error* err; + + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + gpr_free(host); + gpr_free(port); + return err; + } + + /* Call getaddrinfo */ + grpc_custom_resolver resolver; + resolver.host = host; + resolver.port = port; + + grpc_resolved_addresses* addrs; + grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get(); + grpc_core::ExecCtx::Set(nullptr); + err = resolve_address_vtable->resolve(host, port, &addrs); + if (err != GRPC_ERROR_NONE) { + if (retry_named_port_failure(&resolver, &addrs)) { + GRPC_ERROR_UNREF(err); + err = GRPC_ERROR_NONE; + } + } + grpc_core::ExecCtx::Set(curr); + if (err == GRPC_ERROR_NONE) { + *addresses = addrs; + } + gpr_free(resolver.host); + gpr_free(resolver.port); + return err; +} + +static void resolve_address_impl(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { + grpc_custom_resolver* r = nullptr; + char* host = nullptr; + char* port = nullptr; + grpc_error* err; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(on_done, err); + gpr_free(host); + gpr_free(port); + return; + } + r = (grpc_custom_resolver*)gpr_malloc(sizeof(grpc_custom_resolver)); + r->on_done = on_done; + r->addresses = addrs; + r->host = host; + r->port = port; + + /* Call getaddrinfo */ + resolve_address_vtable->resolve_async(r, r->host, r->port); +} + +static grpc_address_resolver_vtable custom_resolver_vtable = { + resolve_address_impl, blocking_resolve_address_impl}; + +void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl) { + resolve_address_vtable = impl; + grpc_set_resolver_impl(&custom_resolver_vtable); +} diff --git a/src/core/lib/iomgr/resolve_address_custom.h b/src/core/lib/iomgr/resolve_address_custom.h new file mode 100644 index 0000000000..e0c6714087 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_custom.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" + +typedef struct grpc_custom_resolver grpc_custom_resolver; + +typedef struct grpc_custom_resolver_vtable { + grpc_error* (*resolve)(char* host, char* port, grpc_resolved_addresses** res); + void (*resolve_async)(grpc_custom_resolver* resolver, char* host, char* port); +} grpc_custom_resolver_vtable; + +void grpc_custom_resolve_callback(grpc_custom_resolver* resolver, + grpc_resolved_addresses* result, + grpc_error* error); + +/* Internal APIs */ +void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl); + +#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H */ diff --git a/src/core/lib/iomgr/resolve_address_posix.cc b/src/core/lib/iomgr/resolve_address_posix.cc index 9307f19bcf..a82075542f 100644 --- a/src/core/lib/iomgr/resolve_address_posix.cc +++ b/src/core/lib/iomgr/resolve_address_posix.cc @@ -35,14 +35,14 @@ #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/block_annotate.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" -static grpc_error* blocking_resolve_address_impl( +static grpc_error* posix_blocking_resolve_address( const char* name, const char* default_port, grpc_resolved_addresses** addresses) { grpc_core::ExecCtx exec_ctx; @@ -141,10 +141,6 @@ done: return err; } -grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) = blocking_resolve_address_impl; - typedef struct { char* name; char* default_port; @@ -165,17 +161,10 @@ static void do_request_thread(void* rp, grpc_error* error) { gpr_free(r); } -void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { - if (addrs != nullptr) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(const char* name, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addrs) { +static void posix_resolve_address(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { request* r = static_cast<request*>(gpr_malloc(sizeof(request))); GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); @@ -186,9 +175,6 @@ static void resolve_address_impl(const char* name, const char* default_port, GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE); } -void (*grpc_resolve_address)( - const char* name, const char* default_port, - grpc_pollset_set* interested_parties, grpc_closure* on_done, - grpc_resolved_addresses** addrs) = resolve_address_impl; - +grpc_address_resolver_vtable grpc_posix_resolver_vtable = { + posix_resolve_address, posix_blocking_resolve_address}; #endif diff --git a/src/core/lib/iomgr/resolve_address_uv.cc b/src/core/lib/iomgr/resolve_address_uv.cc deleted file mode 100644 index 4d8ea596f3..0000000000 --- a/src/core/lib/iomgr/resolve_address_uv.cc +++ /dev/null @@ -1,286 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include <grpc/support/port_platform.h> - -#include "src/core/lib/iomgr/port.h" -#ifdef GRPC_UV - -#include <uv.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> - -#include "src/core/lib/gpr/host_port.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" - -#include <string.h> - -typedef struct request { - grpc_closure* on_done; - grpc_resolved_addresses** addresses; - struct addrinfo* hints; - char* host; - char* port; -} request; - -static int retry_named_port_failure(int status, request* r, - uv_getaddrinfo_cb getaddrinfo_cb) { - if (status != 0) { - // This loop is copied from resolve_address_posix.c - const char* svc[][2] = {{"http", "80"}, {"https", "443"}}; - for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { - if (strcmp(r->port, svc[i][0]) == 0) { - int retry_status; - uv_getaddrinfo_t* req = - (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); - req->data = r; - r->port = gpr_strdup(svc[i][1]); - retry_status = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_cb, - r->host, r->port, r->hints); - if (retry_status < 0 || getaddrinfo_cb == NULL) { - // The callback will not be called - gpr_free(req); - } - return retry_status; - } - } - } - /* If this function calls uv_getaddrinfo, it will return that function's - return value. That function only returns numbers <=0, so we can safely - return 1 to indicate that we never retried */ - return 1; -} - -static grpc_error* handle_addrinfo_result(int status, struct addrinfo* result, - grpc_resolved_addresses** addresses) { - struct addrinfo* resp; - size_t i; - if (status != 0) { - grpc_error* error; - *addresses = NULL; - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - (*addresses) = - (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addresses)->naddrs = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - (*addresses)->naddrs++; - } - (*addresses)->addrs = (grpc_resolved_address*)gpr_malloc( - sizeof(grpc_resolved_address) * (*addresses)->naddrs); - i = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); - (*addresses)->addrs[i].len = resp->ai_addrlen; - i++; - } - - { - for (i = 0; i < (*addresses)->naddrs; i++) { - char* buf; - grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); - gpr_free(buf); - } - } - return GRPC_ERROR_NONE; -} - -static void getaddrinfo_callback(uv_getaddrinfo_t* req, int status, - struct addrinfo* res) { - request* r = (request*)req->data; - grpc_core::ExecCtx exec_ctx; - grpc_error* error; - int retry_status; - char* port = r->port; - - gpr_free(req); - retry_status = retry_named_port_failure(status, r, getaddrinfo_callback); - if (retry_status == 0) { - /* The request is being retried. It is using its own port string, so we free - * the original one */ - gpr_free(port); - return; - } - /* Either no retry was attempted, or the retry failed. Either way, the - original error probably has more interesting information */ - error = handle_addrinfo_result(status, res, r->addresses); - GRPC_CLOSURE_SCHED(r->on_done, error); - - gpr_free(r->hints); - gpr_free(r->host); - gpr_free(r->port); - gpr_free(r); - uv_freeaddrinfo(res); -} - -static grpc_error* try_split_host_port(const char* name, - const char* default_port, char** host, - char** port) { - /* parse name, splitting it into host and port parts */ - grpc_error* error; - gpr_split_host_port(name, host, port); - if (*host == NULL) { - char* msg; - gpr_asprintf(&msg, "unparseable host:port: '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - if (*port == NULL) { - // TODO(murgatroid99): add tests for this case - if (default_port == NULL) { - char* msg; - gpr_asprintf(&msg, "no port in name '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - *port = gpr_strdup(default_port); - } - return GRPC_ERROR_NONE; -} - -static grpc_error* blocking_resolve_address_impl( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) { - char* host; - char* port; - struct addrinfo hints; - uv_getaddrinfo_t req; - int s; - grpc_error* err; - int retry_status; - request r; - - GRPC_UV_ASSERT_SAME_THREAD(); - - req.addrinfo = NULL; - - err = try_split_host_port(name, default_port, &host, &port); - if (err != GRPC_ERROR_NONE) { - goto done; - } - - /* Call getaddrinfo */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints.ai_socktype = SOCK_STREAM; /* stream socket */ - hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ - - s = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); - r.addresses = addresses; - r.hints = &hints; - r.host = host; - r.port = port; - retry_status = retry_named_port_failure(s, &r, NULL); - if (retry_status <= 0) { - s = retry_status; - } - err = handle_addrinfo_result(s, req.addrinfo, addresses); - -done: - gpr_free(host); - gpr_free(port); - if (req.addrinfo) { - uv_freeaddrinfo(req.addrinfo); - } - return err; -} - -grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) = blocking_resolve_address_impl; - -void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(const char* name, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addrs) { - uv_getaddrinfo_t* req = NULL; - request* r = NULL; - struct addrinfo* hints = NULL; - char* host = NULL; - char* port = NULL; - grpc_error* err; - int s; - GRPC_UV_ASSERT_SAME_THREAD(); - err = try_split_host_port(name, default_port, &host, &port); - if (err != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(on_done, err); - gpr_free(host); - gpr_free(port); - return; - } - r = (request*)gpr_malloc(sizeof(request)); - r->on_done = on_done; - r->addresses = addrs; - r->host = host; - r->port = port; - req = (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); - req->data = r; - - /* Call getaddrinfo */ - hints = (addrinfo*)gpr_malloc(sizeof(struct addrinfo)); - memset(hints, 0, sizeof(struct addrinfo)); - hints->ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints->ai_socktype = SOCK_STREAM; /* stream socket */ - hints->ai_flags = AI_PASSIVE; /* for wildcard IP address */ - r->hints = hints; - - s = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_callback, host, port, - hints); - - if (s != 0) { - *addrs = NULL; - err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); - err = grpc_error_set_str(err, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(s))); - GRPC_CLOSURE_SCHED(on_done, err); - gpr_free(r); - gpr_free(req); - gpr_free(hints); - gpr_free(host); - gpr_free(port); - } -} - -void (*grpc_resolve_address)( - const char* name, const char* default_port, - grpc_pollset_set* interested_parties, grpc_closure* on_done, - grpc_resolved_addresses** addrs) = resolve_address_impl; - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/resolve_address_windows.cc b/src/core/lib/iomgr/resolve_address_windows.cc index 4e1dcaabaf..71c92615ad 100644 --- a/src/core/lib/iomgr/resolve_address_windows.cc +++ b/src/core/lib/iomgr/resolve_address_windows.cc @@ -37,7 +37,7 @@ #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/block_annotate.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" @@ -51,7 +51,7 @@ typedef struct { grpc_resolved_addresses** addresses; } request; -static grpc_error* blocking_resolve_address_impl( +static grpc_error* windows_blocking_resolve_address( const char* name, const char* default_port, grpc_resolved_addresses** addresses) { grpc_core::ExecCtx exec_ctx; @@ -130,10 +130,6 @@ done: return error; } -grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) = blocking_resolve_address_impl; - /* Callback to be passed to grpc_executor to asynch-ify * grpc_blocking_resolve_address */ static void do_request_thread(void* rp, grpc_error* error) { @@ -150,17 +146,10 @@ static void do_request_thread(void* rp, grpc_error* error) { gpr_free(r); } -void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(const char* name, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addresses) { +static void windows_resolve_address(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses) { request* r = (request*)gpr_malloc(sizeof(request)); GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); @@ -171,9 +160,6 @@ static void resolve_address_impl(const char* name, const char* default_port, GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE); } -void (*grpc_resolve_address)( - const char* name, const char* default_port, - grpc_pollset_set* interested_parties, grpc_closure* on_done, - grpc_resolved_addresses** addresses) = resolve_address_impl; - +grpc_address_resolver_vtable grpc_windows_resolver_vtable = { + windows_resolve_address, windows_blocking_resolve_address}; #endif diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h index 4e1c651278..89e8a39118 100644 --- a/src/core/lib/iomgr/resource_quota.h +++ b/src/core/lib/iomgr/resource_quota.h @@ -139,8 +139,4 @@ void grpc_resource_user_alloc_slices( grpc_resource_user_slice_allocator* slice_allocator, size_t length, size_t count, grpc_slice_buffer* dest); -/* Allocate one slice of length \a size synchronously. */ -grpc_slice grpc_resource_user_slice_malloc(grpc_resource_user* resource_user, - size_t size); - #endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */ diff --git a/src/core/lib/iomgr/sockaddr.h b/src/core/lib/iomgr/sockaddr.h index 3b30da8a7d..5edf735cd1 100644 --- a/src/core/lib/iomgr/sockaddr.h +++ b/src/core/lib/iomgr/sockaddr.h @@ -25,18 +25,8 @@ #include <grpc/support/port_platform.h> -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV -#include <uv.h> -#endif - -#ifdef GPR_WINDOWS -#include "src/core/lib/iomgr/sockaddr_windows.h" -#endif - -#ifdef GRPC_POSIX_SOCKETADDR +#include "src/core/lib/iomgr/sockaddr_custom.h" #include "src/core/lib/iomgr/sockaddr_posix.h" -#endif +#include "src/core/lib/iomgr/sockaddr_windows.h" #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_H */ diff --git a/src/core/lib/iomgr/sockaddr_custom.h b/src/core/lib/iomgr/sockaddr_custom.h new file mode 100644 index 0000000000..d85cc504d3 --- /dev/null +++ b/src/core/lib/iomgr/sockaddr_custom.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include <stddef.h> +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <uv.h> + +// TODO(kpayson) It would be nice to abstract this so we don't +// depend on anything uv specific +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif // GRPC_UV + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H */ diff --git a/src/core/lib/iomgr/sockaddr_posix.h b/src/core/lib/iomgr/sockaddr_posix.h index 83981e0aa5..5b18bbc465 100644 --- a/src/core/lib/iomgr/sockaddr_posix.h +++ b/src/core/lib/iomgr/sockaddr_posix.h @@ -21,6 +21,9 @@ #include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET #include <arpa/inet.h> #include <netdb.h> #include <netinet/in.h> @@ -28,4 +31,25 @@ #include <sys/types.h> #include <unistd.h> +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif + #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H */ diff --git a/src/core/lib/iomgr/sockaddr_utils.cc b/src/core/lib/iomgr/sockaddr_utils.cc index 88f9b2ffd9..bc3550a679 100644 --- a/src/core/lib/iomgr/sockaddr_utils.cc +++ b/src/core/lib/iomgr/sockaddr_utils.cc @@ -40,25 +40,25 @@ static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* resolved_addr, grpc_resolved_address* resolved_addr4_out) { GPR_ASSERT(resolved_addr != resolved_addr4_out); - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - struct sockaddr_in* addr4_out = + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + grpc_sockaddr_in* addr4_out = resolved_addr4_out == nullptr ? nullptr - : reinterpret_cast<struct sockaddr_in*>(resolved_addr4_out->addr); - if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6* addr6 = - reinterpret_cast<const struct sockaddr_in6*>(addr); + : reinterpret_cast<grpc_sockaddr_in*>(resolved_addr4_out->addr); + if (addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast<const grpc_sockaddr_in6*>(addr); if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, sizeof(kV4MappedPrefix)) == 0) { if (resolved_addr4_out != nullptr) { /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out)); - addr4_out->sin_family = AF_INET; + addr4_out->sin_family = GRPC_AF_INET; /* s6_addr32 would be nice, but it's non-standard. */ memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4); addr4_out->sin_port = addr6->sin6_port; - resolved_addr4_out->len = sizeof(struct sockaddr_in); + resolved_addr4_out->len = sizeof(grpc_sockaddr_in); } return 1; } @@ -69,19 +69,19 @@ int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* resolved_addr, int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* resolved_addr, grpc_resolved_address* resolved_addr6_out) { GPR_ASSERT(resolved_addr != resolved_addr6_out); - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - struct sockaddr_in6* addr6_out = - reinterpret_cast<struct sockaddr_in6*>(resolved_addr6_out->addr); - if (addr->sa_family == AF_INET) { - const struct sockaddr_in* addr4 = - reinterpret_cast<const struct sockaddr_in*>(addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + grpc_sockaddr_in6* addr6_out = + reinterpret_cast<grpc_sockaddr_in6*>(resolved_addr6_out->addr); + if (addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast<const grpc_sockaddr_in*>(addr); memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out)); - addr6_out->sin6_family = AF_INET6; + addr6_out->sin6_family = GRPC_AF_INET6; memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12); memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4); addr6_out->sin6_port = addr4->sin_port; - resolved_addr6_out->len = sizeof(struct sockaddr_in6); + resolved_addr6_out->len = sizeof(grpc_sockaddr_in6); return 1; } return 0; @@ -89,32 +89,32 @@ int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* resolved_addr, int grpc_sockaddr_is_wildcard(const grpc_resolved_address* resolved_addr, int* port_out) { - const struct sockaddr* addr; + const grpc_sockaddr* addr; grpc_resolved_address addr4_normalized; if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) { resolved_addr = &addr4_normalized; } - addr = reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - if (addr->sa_family == AF_INET) { + addr = reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + if (addr->sa_family == GRPC_AF_INET) { /* Check for 0.0.0.0 */ - const struct sockaddr_in* addr4 = - reinterpret_cast<const struct sockaddr_in*>(addr); + const grpc_sockaddr_in* addr4 = + reinterpret_cast<const grpc_sockaddr_in*>(addr); if (addr4->sin_addr.s_addr != 0) { return 0; } - *port_out = ntohs(addr4->sin_port); + *port_out = grpc_ntohs(addr4->sin_port); return 1; - } else if (addr->sa_family == AF_INET6) { + } else if (addr->sa_family == GRPC_AF_INET6) { /* Check for :: */ - const struct sockaddr_in6* addr6 = - reinterpret_cast<const struct sockaddr_in6*>(addr); + const grpc_sockaddr_in6* addr6 = + reinterpret_cast<const grpc_sockaddr_in6*>(addr); int i; for (i = 0; i < 16; i++) { if (addr6->sin6_addr.s6_addr[i] != 0) { return 0; } } - *port_out = ntohs(addr6->sin6_port); + *port_out = grpc_ntohs(addr6->sin6_port); return 1; } else { return 0; @@ -129,33 +129,33 @@ void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address* wild4_out, void grpc_sockaddr_make_wildcard4(int port, grpc_resolved_address* resolved_wild_out) { - struct sockaddr_in* wild_out = - reinterpret_cast<struct sockaddr_in*>(resolved_wild_out->addr); + grpc_sockaddr_in* wild_out = + reinterpret_cast<grpc_sockaddr_in*>(resolved_wild_out->addr); GPR_ASSERT(port >= 0 && port < 65536); memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); - wild_out->sin_family = AF_INET; - wild_out->sin_port = htons(static_cast<uint16_t>(port)); - resolved_wild_out->len = sizeof(struct sockaddr_in); + wild_out->sin_family = GRPC_AF_INET; + wild_out->sin_port = grpc_htons(static_cast<uint16_t>(port)); + resolved_wild_out->len = sizeof(grpc_sockaddr_in); } void grpc_sockaddr_make_wildcard6(int port, grpc_resolved_address* resolved_wild_out) { - struct sockaddr_in6* wild_out = - reinterpret_cast<struct sockaddr_in6*>(resolved_wild_out->addr); + grpc_sockaddr_in6* wild_out = + reinterpret_cast<grpc_sockaddr_in6*>(resolved_wild_out->addr); GPR_ASSERT(port >= 0 && port < 65536); memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); - wild_out->sin6_family = AF_INET6; - wild_out->sin6_port = htons(static_cast<uint16_t>(port)); - resolved_wild_out->len = sizeof(struct sockaddr_in6); + wild_out->sin6_family = GRPC_AF_INET6; + wild_out->sin6_port = grpc_htons(static_cast<uint16_t>(port)); + resolved_wild_out->len = sizeof(grpc_sockaddr_in6); } int grpc_sockaddr_to_string(char** out, const grpc_resolved_address* resolved_addr, int normalize) { - const struct sockaddr* addr; + const grpc_sockaddr* addr; const int save_errno = errno; grpc_resolved_address addr_normalized; - char ntop_buf[INET6_ADDRSTRLEN]; + char ntop_buf[GRPC_INET6_ADDRSTRLEN]; const void* ip = nullptr; int port = 0; uint32_t sin6_scope_id = 0; @@ -165,17 +165,17 @@ int grpc_sockaddr_to_string(char** out, if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { resolved_addr = &addr_normalized; } - addr = reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - if (addr->sa_family == AF_INET) { - const struct sockaddr_in* addr4 = - reinterpret_cast<const struct sockaddr_in*>(addr); + addr = reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + if (addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast<const grpc_sockaddr_in*>(addr); ip = &addr4->sin_addr; - port = ntohs(addr4->sin_port); - } else if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6* addr6 = - reinterpret_cast<const struct sockaddr_in6*>(addr); + port = grpc_ntohs(addr4->sin_port); + } else if (addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast<const grpc_sockaddr_in6*>(addr); ip = &addr6->sin6_addr; - port = ntohs(addr6->sin6_port); + port = grpc_ntohs(addr6->sin6_port); sin6_scope_id = addr6->sin6_scope_id; } if (ip != nullptr && grpc_inet_ntop(addr->sa_family, ip, ntop_buf, @@ -197,6 +197,22 @@ int grpc_sockaddr_to_string(char** out, return ret; } +void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port) { + grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)out->addr; + grpc_sockaddr_in* addr4 = (grpc_sockaddr_in*)out->addr; + + if (grpc_inet_pton(GRPC_AF_INET6, addr, &addr6->sin6_addr) == 1) { + addr6->sin6_family = GRPC_AF_INET6; + out->len = sizeof(grpc_sockaddr_in6); + } else if (grpc_inet_pton(GRPC_AF_INET, addr, &addr4->sin_addr) == 1) { + addr4->sin_family = GRPC_AF_INET; + out->len = sizeof(grpc_sockaddr_in); + } else { + GPR_ASSERT(0); + } + grpc_sockaddr_set_port(out, port); +} + char* grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) { grpc_resolved_address addr_normalized; if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { @@ -219,33 +235,33 @@ char* grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) { const char* grpc_sockaddr_get_uri_scheme( const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); switch (addr->sa_family) { - case AF_INET: + case GRPC_AF_INET: return "ipv4"; - case AF_INET6: + case GRPC_AF_INET6: return "ipv6"; - case AF_UNIX: + case GRPC_AF_UNIX: return "unix"; } return nullptr; } int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); return addr->sa_family; } int grpc_sockaddr_get_port(const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); switch (addr->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in*)addr)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6*)addr)->sin6_port); + case GRPC_AF_INET: + return grpc_ntohs(((grpc_sockaddr_in*)addr)->sin_port); + case GRPC_AF_INET6: + return grpc_ntohs(((grpc_sockaddr_in6*)addr)->sin6_port); default: if (grpc_is_unix_socket(resolved_addr)) { return 1; @@ -258,18 +274,18 @@ int grpc_sockaddr_get_port(const grpc_resolved_address* resolved_addr) { int grpc_sockaddr_set_port(const grpc_resolved_address* resolved_addr, int port) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); switch (addr->sa_family) { - case AF_INET: + case GRPC_AF_INET: GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in*)addr)->sin_port = - htons(static_cast<uint16_t>(port)); + ((grpc_sockaddr_in*)addr)->sin_port = + grpc_htons(static_cast<uint16_t>(port)); return 1; - case AF_INET6: + case GRPC_AF_INET6: GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in6*)addr)->sin6_port = - htons(static_cast<uint16_t>(port)); + ((grpc_sockaddr_in6*)addr)->sin6_port = + grpc_htons(static_cast<uint16_t>(port)); return 1; default: gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", diff --git a/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h index ace54a2a80..a4e90a73ab 100644 --- a/src/core/lib/iomgr/sockaddr_utils.h +++ b/src/core/lib/iomgr/sockaddr_utils.h @@ -71,6 +71,8 @@ int grpc_sockaddr_set_port(const grpc_resolved_address* addr, int port); int grpc_sockaddr_to_string(char** out, const grpc_resolved_address* addr, int normalize); +void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port); + /* Returns the URI string corresponding to \a addr */ char* grpc_sockaddr_to_uri(const grpc_resolved_address* addr); diff --git a/src/core/lib/iomgr/sockaddr_windows.h b/src/core/lib/iomgr/sockaddr_windows.h index 3a4fcc9e8a..4d637251a1 100644 --- a/src/core/lib/iomgr/sockaddr_windows.h +++ b/src/core/lib/iomgr/sockaddr_windows.h @@ -31,6 +31,25 @@ // must be included after the above #include <mswsock.h> +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + #endif #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H */ diff --git a/src/core/lib/iomgr/socket_utils.h b/src/core/lib/iomgr/socket_utils.h index e96eb97a7e..cf1a7be648 100644 --- a/src/core/lib/iomgr/socket_utils.h +++ b/src/core/lib/iomgr/socket_utils.h @@ -23,6 +23,15 @@ #include <stddef.h> +/* A wrapper for htons on POSIX and Windows */ +uint16_t grpc_htons(uint16_t hostshort); + +/* A wrapper for ntohs on POSIX and WINDOWS */ +uint16_t grpc_ntohs(uint16_t netshort); + +/* A wrapper for inet_pton on POSIX and WINDOWS */ +int grpc_inet_pton(int af, const char* src, void* dst); + /* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */ const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size); diff --git a/src/core/lib/iomgr/socket_utils_common_posix.cc b/src/core/lib/iomgr/socket_utils_common_posix.cc index 4fb6c7ad63..c52e237fa8 100644 --- a/src/core/lib/iomgr/socket_utils_common_posix.cc +++ b/src/core/lib/iomgr/socket_utils_common_posix.cc @@ -43,6 +43,7 @@ #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" /* set a socket to non blocking mode */ @@ -215,12 +216,11 @@ static void probe_ipv6_once(void) { if (fd < 0) { gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed."); } else { - struct sockaddr_in6 addr; + grpc_sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */ - if (bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == - 0) { + if (bind(fd, reinterpret_cast<grpc_sockaddr*>(&addr), sizeof(addr)) == 0) { g_ipv6_loopback_available = 1; } else { gpr_log(GPR_INFO, @@ -280,8 +280,8 @@ static int create_socket(grpc_socket_factory* factory, int domain, int type, grpc_error* grpc_create_dualstack_socket_using_factory( grpc_socket_factory* factory, const grpc_resolved_address* resolved_addr, int type, int protocol, grpc_dualstack_mode* dsmode, int* newfd) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); int family = addr->sa_family; if (family == AF_INET6) { if (grpc_ipv6_loopback_available()) { @@ -311,6 +311,14 @@ grpc_error* grpc_create_dualstack_socket_using_factory( return error_for_fd(*newfd, resolved_addr); } +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { GPR_ASSERT(size <= (socklen_t)-1); return inet_ntop(af, src, dst, static_cast<socklen_t>(size)); diff --git a/src/core/lib/iomgr/socket_utils_linux.cc b/src/core/lib/iomgr/socket_utils_linux.cc index deb7c55267..1364cd35f6 100644 --- a/src/core/lib/iomgr/socket_utils_linux.cc +++ b/src/core/lib/iomgr/socket_utils_linux.cc @@ -37,8 +37,7 @@ int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); flags |= nonblock ? SOCK_NONBLOCK : 0; flags |= cloexec ? SOCK_CLOEXEC : 0; - return accept4(sockfd, - reinterpret_cast<struct sockaddr*>(resolved_addr->addr), + return accept4(sockfd, reinterpret_cast<grpc_sockaddr*>(resolved_addr->addr), reinterpret_cast<socklen_t*>(&resolved_addr->len), flags); } diff --git a/src/core/lib/iomgr/socket_utils_posix.cc b/src/core/lib/iomgr/socket_utils_posix.cc index c856f641e3..d5d00af976 100644 --- a/src/core/lib/iomgr/socket_utils_posix.cc +++ b/src/core/lib/iomgr/socket_utils_posix.cc @@ -36,7 +36,7 @@ int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, int fd, flags; GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); - fd = accept(sockfd, (struct sockaddr*)resolved_addr->addr, + fd = accept(sockfd, (grpc_sockaddr*)resolved_addr->addr, (socklen_t*)&resolved_addr->len); if (fd >= 0) { if (nonblock) { diff --git a/src/core/lib/iomgr/socket_utils_uv.cc b/src/core/lib/iomgr/socket_utils_uv.cc index 3f650eef66..8538abc7e4 100644 --- a/src/core/lib/iomgr/socket_utils_uv.cc +++ b/src/core/lib/iomgr/socket_utils_uv.cc @@ -22,15 +22,24 @@ #ifdef GRPC_UV -#include <uv.h> - +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/socket_utils.h" #include <grpc/support/log.h> +#include <uv.h> + +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { - uv_inet_ntop(af, src, dst, size); - return dst; + /* Windows InetNtopA wants a mutable ip pointer */ + return inet_ntop(af, src, dst, (socklen_t)size); } #endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/socket_utils_windows.cc b/src/core/lib/iomgr/socket_utils_windows.cc index 5fc3b7617e..3e7b5b812d 100644 --- a/src/core/lib/iomgr/socket_utils_windows.cc +++ b/src/core/lib/iomgr/socket_utils_windows.cc @@ -27,6 +27,14 @@ #include <grpc/support/log.h> +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { /* Windows InetNtopA wants a mutable ip pointer */ return InetNtopA(af, (void*)src, dst, size); diff --git a/src/core/lib/iomgr/tcp_client.cc b/src/core/lib/iomgr/tcp_client.cc new file mode 100644 index 0000000000..6c0ba40781 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/tcp_client.h" + +grpc_tcp_client_vtable* grpc_tcp_client_impl; + +void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { + grpc_tcp_client_impl->connect(closure, ep, interested_parties, channel_args, + addr, deadline); +} + +void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl) { + grpc_tcp_client_impl = impl; +} diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h index a6b99e63c2..d209eeb8c2 100644 --- a/src/core/lib/iomgr/tcp_client.h +++ b/src/core/lib/iomgr/tcp_client.h @@ -27,6 +27,13 @@ #include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/resolve_address.h" +typedef struct grpc_tcp_client_vtable { + void (*connect)(grpc_closure* on_connect, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, grpc_millis deadline); +} grpc_tcp_client_vtable; + /* Asynchronously connect to an address (specified as (addr, len)), and call cb with arg and the completed connection when done (or call cb with arg and NULL on failure). @@ -38,4 +45,8 @@ void grpc_tcp_client_connect(grpc_closure* on_connect, grpc_endpoint** endpoint, const grpc_resolved_address* addr, grpc_millis deadline); +void grpc_tcp_client_global_init(); + +void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl); + #endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */ diff --git a/src/core/lib/iomgr/tcp_client_custom.cc b/src/core/lib/iomgr/tcp_client_custom.cc new file mode 100644 index 0000000000..55632a55a1 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client_custom.cc @@ -0,0 +1,151 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; +extern grpc_socket_vtable* grpc_custom_socket_vtable; + +struct grpc_custom_tcp_connect { + grpc_custom_socket* socket; + grpc_timer alarm; + grpc_closure on_alarm; + grpc_closure* closure; + grpc_endpoint** endpoint; + int refs; + char* addr_name; + grpc_resource_quota* resource_quota; +}; + +static void custom_tcp_connect_cleanup(grpc_custom_tcp_connect* connect) { + grpc_custom_socket* socket = connect->socket; + grpc_resource_quota_unref_internal(connect->resource_quota); + gpr_free(connect->addr_name); + gpr_free(connect); + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } +} + +static void custom_close_callback(grpc_custom_socket* socket) {} + +static void on_alarm(void* acp, grpc_error* error) { + int done; + grpc_custom_socket* socket = (grpc_custom_socket*)acp; + grpc_custom_tcp_connect* connect = socket->connector; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", + connect->addr_name, str); + } + if (error == GRPC_ERROR_NONE) { + /* error == NONE implies that the timer ran out, and wasn't cancelled. If + it was cancelled, then the handler that cancelled it also should close + the handle, if applicable */ + grpc_custom_socket_vtable->close(socket, custom_close_callback); + } + done = (--connect->refs == 0); + if (done) { + custom_tcp_connect_cleanup(connect); + } +} + +static void custom_connect_callback(grpc_custom_socket* socket, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_custom_tcp_connect* connect = socket->connector; + int done; + grpc_closure* closure = connect->closure; + grpc_timer_cancel(&connect->alarm); + if (error == GRPC_ERROR_NONE) { + *connect->endpoint = custom_tcp_endpoint_create( + socket, connect->resource_quota, connect->addr_name); + } + done = (--connect->refs == 0); + if (done) { + grpc_core::ExecCtx::Get()->Flush(); + custom_tcp_connect_cleanup(connect); + } + GRPC_CLOSURE_SCHED(closure, error); +} + +static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* resolved_addr, + grpc_millis deadline) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + (void)channel_args; + (void)interested_parties; + grpc_custom_tcp_connect* connect; + grpc_resource_quota* resource_quota = grpc_resource_quota_create(nullptr); + if (channel_args != nullptr) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)channel_args->args[i].value.pointer.p); + } + } + } + grpc_custom_socket* socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + socket->refs = 2; + grpc_custom_socket_vtable->init(socket, GRPC_AF_UNSPEC); + connect = + (grpc_custom_tcp_connect*)gpr_malloc(sizeof(grpc_custom_tcp_connect)); + connect->closure = closure; + connect->endpoint = ep; + connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); + connect->resource_quota = resource_quota; + connect->socket = socket; + socket->connector = connect; + socket->endpoint = nullptr; + socket->listener = nullptr; + connect->refs = 2; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %p %s: asynchronously connecting", + socket, connect->addr_name); + } + + grpc_custom_socket_vtable->connect( + socket, (const grpc_sockaddr*)resolved_addr->addr, resolved_addr->len, + custom_connect_callback); + GRPC_CLOSURE_INIT(&connect->on_alarm, on_alarm, socket, + grpc_schedule_on_exec_ctx); + grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm); +} + +grpc_tcp_client_vtable custom_tcp_client_vtable = {tcp_connect}; diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc index 3fe2989c6b..c21fb40ab1 100644 --- a/src/core/lib/iomgr/tcp_client_posix.cc +++ b/src/core/lib/iomgr/tcp_client_posix.cc @@ -38,6 +38,7 @@ #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/socket_mutator.h" #include "src/core/lib/iomgr/socket_utils_posix.h" @@ -293,7 +294,7 @@ void grpc_tcp_client_create_from_prepared_fd( async_connect* ac; do { GPR_ASSERT(addr->len < ~(socklen_t)0); - err = connect(fd, reinterpret_cast<const struct sockaddr*>(addr->addr), + err = connect(fd, reinterpret_cast<const grpc_sockaddr*>(addr->addr), static_cast<socklen_t>(addr->len)); } while (err < 0 && errno == EINTR); if (err >= 0) { @@ -336,11 +337,11 @@ void grpc_tcp_client_create_from_prepared_fd( gpr_mu_unlock(&ac->mu); } -static void tcp_client_connect_impl(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { +static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { grpc_resolved_address mapped_addr; grpc_fd* fdobj = nullptr; grpc_error* error; @@ -355,20 +356,5 @@ static void tcp_client_connect_impl(grpc_closure* closure, grpc_endpoint** ep, ep); } -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { - grpc_tcp_client_connect_impl(closure, ep, interested_parties, channel_args, - addr, deadline); -} - +grpc_tcp_client_vtable grpc_posix_tcp_client_vtable = {tcp_connect}; #endif diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc deleted file mode 100644 index d29d6c8f41..0000000000 --- a/src/core/lib/iomgr/tcp_client_uv.cc +++ /dev/null @@ -1,177 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include <grpc/support/port_platform.h> - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/iomgr/tcp_uv.h" -#include "src/core/lib/iomgr/timer.h" - -extern grpc_core::TraceFlag grpc_tcp_trace; - -typedef struct grpc_uv_tcp_connect { - uv_connect_t connect_req; - grpc_timer alarm; - grpc_closure on_alarm; - uv_tcp_t* tcp_handle; - grpc_closure* closure; - grpc_endpoint** endpoint; - int refs; - char* addr_name; - grpc_resource_quota* resource_quota; -} grpc_uv_tcp_connect; - -static void uv_tcp_connect_cleanup(grpc_uv_tcp_connect* connect) { - grpc_resource_quota_unref_internal(connect->resource_quota); - gpr_free(connect->addr_name); - gpr_free(connect); -} - -static void tcp_close_callback(uv_handle_t* handle) { gpr_free(handle); } - -static void uv_tc_on_alarm(void* acp, grpc_error* error) { - int done; - grpc_uv_tcp_connect* connect = (grpc_uv_tcp_connect*)acp; - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", - connect->addr_name, str); - } - if (error == GRPC_ERROR_NONE) { - /* error == NONE implies that the timer ran out, and wasn't cancelled. If - it was cancelled, then the handler that cancelled it also should close - the handle, if applicable */ - uv_close((uv_handle_t*)connect->tcp_handle, tcp_close_callback); - } - done = (--connect->refs == 0); - if (done) { - uv_tcp_connect_cleanup(connect); - } -} - -static void uv_tc_on_connect(uv_connect_t* req, int status) { - grpc_uv_tcp_connect* connect = (grpc_uv_tcp_connect*)req->data; - grpc_core::ExecCtx exec_ctx; - grpc_error* error = GRPC_ERROR_NONE; - int done; - grpc_closure* closure = connect->closure; - grpc_timer_cancel(&connect->alarm); - if (status == 0) { - *connect->endpoint = grpc_tcp_create( - connect->tcp_handle, connect->resource_quota, connect->addr_name); - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to connect to remote host"); - error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, -status); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - if (status == UV_ECANCELED) { - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string("Timeout occurred")); - // This should only happen if the handle is already closed - } else { - error = grpc_error_set_str( - error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - uv_close((uv_handle_t*)connect->tcp_handle, tcp_close_callback); - } - } - done = (--connect->refs == 0); - if (done) { - grpc_core::ExecCtx::Get()->Flush(); - uv_tcp_connect_cleanup(connect); - } - GRPC_CLOSURE_SCHED(closure, error); -} - -static void tcp_client_connect_impl(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* resolved_addr, - grpc_millis deadline) { - grpc_uv_tcp_connect* connect; - grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL); - (void)channel_args; - (void)interested_parties; - - GRPC_UV_ASSERT_SAME_THREAD(); - - if (channel_args != NULL) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { - grpc_resource_quota_unref_internal(resource_quota); - resource_quota = grpc_resource_quota_ref_internal( - (grpc_resource_quota*)channel_args->args[i].value.pointer.p); - } - } - } - - connect = (grpc_uv_tcp_connect*)gpr_zalloc(sizeof(grpc_uv_tcp_connect)); - connect->closure = closure; - connect->endpoint = ep; - connect->tcp_handle = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); - connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); - connect->resource_quota = resource_quota; - uv_tcp_init(uv_default_loop(), connect->tcp_handle); - connect->connect_req.data = connect; - connect->refs = 2; // One for the connect operation, one for the timer. - - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting", - connect->addr_name); - } - - // TODO(murgatroid99): figure out what the return value here means - uv_tcp_connect(&connect->connect_req, connect->tcp_handle, - (const struct sockaddr*)resolved_addr->addr, uv_tc_on_connect); - GRPC_CLOSURE_INIT(&connect->on_alarm, uv_tc_on_alarm, connect, - grpc_schedule_on_exec_ctx); - grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm); -} - -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { - grpc_tcp_client_connect_impl(closure, ep, interested_parties, channel_args, - addr, deadline); -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc index 70c2495350..e5b5502597 100644 --- a/src/core/lib/iomgr/tcp_client_windows.cc +++ b/src/core/lib/iomgr/tcp_client_windows.cc @@ -122,12 +122,11 @@ static void on_connect(void* acp, grpc_error* error) { /* Tries to issue one async connection, then schedules both an IOCP notification request for the connection, and one timeout alert. */ -static void tcp_client_connect_impl(grpc_closure* on_done, - grpc_endpoint** endpoint, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { +static void tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { SOCKET sock = INVALID_SOCKET; BOOL success; int status; @@ -175,7 +174,7 @@ static void tcp_client_connect_impl(grpc_closure* on_done, grpc_sockaddr_make_wildcard6(0, &local_address); status = - bind(sock, (struct sockaddr*)&local_address.addr, (int)local_address.len); + bind(sock, (grpc_sockaddr*)&local_address.addr, (int)local_address.len); if (status != 0) { error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); goto failure; @@ -183,7 +182,7 @@ static void tcp_client_connect_impl(grpc_closure* on_done, socket = grpc_winsocket_create(sock, "client"); info = &socket->write_info; - success = ConnectEx(sock, (struct sockaddr*)&addr->addr, (int)addr->len, NULL, + success = ConnectEx(sock, (grpc_sockaddr*)&addr->addr, (int)addr->len, NULL, 0, NULL, &info->overlapped); /* It wouldn't be unusual to get a success immediately. But we'll still get @@ -227,20 +226,6 @@ failure: GRPC_CLOSURE_SCHED(on_done, final_error); } -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { - grpc_tcp_client_connect_impl(closure, ep, interested_parties, channel_args, - addr, deadline); -} +grpc_tcp_client_vtable grpc_windows_tcp_client_vtable = {tcp_connect}; #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_custom.cc b/src/core/lib/iomgr/tcp_custom.cc new file mode 100644 index 0000000000..2b1fc93028 --- /dev/null +++ b/src/core/lib/iomgr/tcp_custom.cc @@ -0,0 +1,365 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <limits.h> +#include <string.h> + +#include <grpc/slice_buffer.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 + +extern grpc_core::TraceFlag grpc_tcp_trace; + +grpc_socket_vtable* grpc_custom_socket_vtable = nullptr; +extern grpc_tcp_server_vtable custom_tcp_server_vtable; +extern grpc_tcp_client_vtable custom_tcp_client_vtable; + +void grpc_custom_endpoint_init(grpc_socket_vtable* impl) { + grpc_custom_socket_vtable = impl; + grpc_set_tcp_client_impl(&custom_tcp_client_vtable); + grpc_set_tcp_server_impl(&custom_tcp_server_vtable); +} + +typedef struct { + grpc_endpoint base; + gpr_refcount refcount; + grpc_custom_socket* socket; + + grpc_closure* read_cb; + grpc_closure* write_cb; + + grpc_slice_buffer* read_slices; + grpc_slice_buffer* write_slices; + + grpc_resource_user* resource_user; + grpc_resource_user_slice_allocator slice_allocator; + + bool shutting_down; + + char* peer_string; +} custom_tcp_endpoint; + +static void tcp_free(grpc_custom_socket* s) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)s->endpoint; + grpc_resource_user_unref(tcp->resource_user); + gpr_free(tcp->peer_string); + gpr_free(tcp); + s->refs--; + if (s->refs == 0) { + grpc_custom_socket_vtable->destroy(s); + gpr_free(s); + } +} + +#ifndef NDEBUG +#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(custom_tcp_endpoint* tcp, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason, + val, val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp->socket); + } +} + +static void tcp_ref(custom_tcp_endpoint* tcp, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason, + val, val + 1); + } + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(custom_tcp_endpoint* tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp->socket); + } +} + +static void tcp_ref(custom_tcp_endpoint* tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void call_read_cb(custom_tcp_endpoint* tcp, grpc_error* error) { + grpc_closure* cb = tcp->read_cb; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp->socket, cb, cb->cb, + cb->cb_arg); + size_t i; + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "read: error=%s", str); + + for (i = 0; i < tcp->read_slices->count; i++) { + char* dump = grpc_dump_slice(tcp->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); + gpr_free(dump); + } + } + TCP_UNREF(tcp, "read"); + tcp->read_slices = nullptr; + tcp->read_cb = nullptr; + GRPC_CLOSURE_RUN(cb, error); +} + +static void custom_read_callback(grpc_custom_socket* socket, size_t nread, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_buffer garbage; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + if (error == GRPC_ERROR_NONE && nread == 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } + if (error == GRPC_ERROR_NONE) { + // Successful read + if ((size_t)nread < tcp->read_slices->length) { + /* TODO(murgatroid99): Instead of discarding the unused part of the read + * buffer, reuse it as the next read buffer. */ + grpc_slice_buffer_init(&garbage); + grpc_slice_buffer_trim_end( + tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage); + grpc_slice_buffer_reset_and_unref_internal(&garbage); + } + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + } + call_read_cb(tcp, error); +} + +static void tcp_read_allocation_done(void* tcpp, grpc_error* error) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)tcpp; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp->socket, + grpc_error_string(error)); + } + if (error == GRPC_ERROR_NONE) { + /* Before calling read, we allocate a buffer with exactly one slice + * to tcp->read_slices and wait for the callback indicating that the + * allocation was successful. So slices[0] should always exist here */ + char* buffer = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]); + size_t len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]); + grpc_custom_socket_vtable->read(tcp->socket, buffer, len, + custom_read_callback); + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + call_read_cb(tcp, GRPC_ERROR_REF(error)); + } + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp->socket, str); + } +} + +static void endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices, + grpc_closure* cb) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + GPR_ASSERT(tcp->read_cb == nullptr); + tcp->read_cb = cb; + tcp->read_slices = read_slices; + grpc_slice_buffer_reset_and_unref_internal(read_slices); + TCP_REF(tcp, "read"); + grpc_resource_user_alloc_slices(&tcp->slice_allocator, + GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, + tcp->read_slices); +} + +static void custom_write_callback(grpc_custom_socket* socket, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + grpc_closure* cb = tcp->write_cb; + tcp->write_cb = nullptr; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp->socket, str); + } + TCP_UNREF(tcp, "write"); + GRPC_CLOSURE_SCHED(cb, error); +} + +static void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* write_slices, + grpc_closure* cb) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + if (grpc_tcp_trace.enabled()) { + size_t j; + + for (j = 0; j < write_slices->count; j++) { + char* data = grpc_dump_slice(write_slices->slices[j], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp->socket, + tcp->peer_string, data); + gpr_free(data); + } + } + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TCP socket is shutting down")); + return; + } + + GPR_ASSERT(tcp->write_cb == nullptr); + tcp->write_slices = write_slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + if (tcp->write_slices->count == 0) { + // No slices means we don't have to do anything, + // and libuv doesn't like empty writes + GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_NONE); + return; + } + tcp->write_cb = cb; + TCP_REF(tcp, "write"); + grpc_custom_socket_vtable->write(tcp->socket, tcp->write_slices, + custom_write_callback); +} + +static void endpoint_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_add_to_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + if (!tcp->shutting_down) { + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(why); + gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->socket, str); + } + tcp->shutting_down = true; + // GRPC_CLOSURE_SCHED(tcp->read_cb, GRPC_ERROR_REF(why)); + // GRPC_CLOSURE_SCHED(tcp->write_cb, GRPC_ERROR_REF(why)); + // tcp->read_cb = nullptr; + // tcp->write_cb = nullptr; + grpc_resource_user_shutdown(tcp->resource_user); + grpc_custom_socket_vtable->shutdown(tcp->socket); + } + GRPC_ERROR_UNREF(why); +} + +static void custom_close_callback(grpc_custom_socket* socket) { + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } else if (socket->endpoint) { + grpc_core::ExecCtx exec_ctx; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + TCP_UNREF(tcp, "destroy"); + } +} + +static void endpoint_destroy(grpc_endpoint* ep) { + grpc_network_status_unregister_endpoint(ep); + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + grpc_custom_socket_vtable->close(tcp->socket, custom_close_callback); +} + +static char* endpoint_get_peer(grpc_endpoint* ep) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_resource_user* endpoint_get_resource_user(grpc_endpoint* ep) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + return tcp->resource_user; +} + +static int endpoint_get_fd(grpc_endpoint* ep) { return -1; } + +static grpc_endpoint_vtable vtable = {endpoint_read, + endpoint_write, + endpoint_add_to_pollset, + endpoint_add_to_pollset_set, + endpoint_delete_from_pollset_set, + endpoint_shutdown, + endpoint_destroy, + endpoint_get_resource_user, + endpoint_get_peer, + endpoint_get_fd}; + +grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket, + grpc_resource_quota* resource_quota, + char* peer_string) { + custom_tcp_endpoint* tcp = + (custom_tcp_endpoint*)gpr_malloc(sizeof(custom_tcp_endpoint)); + grpc_core::ExecCtx exec_ctx; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", socket); + } + memset(tcp, 0, sizeof(custom_tcp_endpoint)); + socket->refs++; + socket->endpoint = (grpc_endpoint*)tcp; + tcp->socket = socket; + tcp->base.vtable = &vtable; + gpr_ref_init(&tcp->refcount, 1); + tcp->peer_string = gpr_strdup(peer_string); + tcp->shutting_down = false; + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + grpc_resource_user_slice_allocator_init( + &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + + return &tcp->base; +} diff --git a/src/core/lib/iomgr/tcp_custom.h b/src/core/lib/iomgr/tcp_custom.h new file mode 100644 index 0000000000..22caa149f8 --- /dev/null +++ b/src/core/lib/iomgr/tcp_custom.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/sockaddr.h" + +typedef struct grpc_tcp_listener grpc_tcp_listener; +typedef struct grpc_custom_tcp_connect grpc_custom_tcp_connect; + +typedef struct grpc_custom_socket { + // Implementation defined + void* impl; + grpc_endpoint* endpoint; + grpc_tcp_listener* listener; + grpc_custom_tcp_connect* connector; + int refs; +} grpc_custom_socket; + +typedef void (*grpc_custom_connect_callback)(grpc_custom_socket* socket, + grpc_error* error); +typedef void (*grpc_custom_write_callback)(grpc_custom_socket* socket, + grpc_error* error); +typedef void (*grpc_custom_read_callback)(grpc_custom_socket* socket, + size_t nread, grpc_error* error); +typedef void (*grpc_custom_accept_callback)(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error); +typedef void (*grpc_custom_close_callback)(grpc_custom_socket* socket); + +typedef struct grpc_socket_vtable { + grpc_error* (*init)(grpc_custom_socket* socket, int domain); + void (*connect)(grpc_custom_socket* socket, const grpc_sockaddr* addr, + size_t len, grpc_custom_connect_callback cb); + void (*destroy)(grpc_custom_socket* socket); + void (*shutdown)(grpc_custom_socket* socket); + void (*close)(grpc_custom_socket* socket, grpc_custom_close_callback cb); + void (*write)(grpc_custom_socket* socket, grpc_slice_buffer* slices, + grpc_custom_write_callback cb); + void (*read)(grpc_custom_socket* socket, char* buffer, size_t length, + grpc_custom_read_callback cb); + grpc_error* (*getpeername)(grpc_custom_socket* socket, + const grpc_sockaddr* addr, int* len); + grpc_error* (*getsockname)(grpc_custom_socket* socket, + const grpc_sockaddr* addr, int* len); + grpc_error* (*setsockopt)(grpc_custom_socket* socket, int level, int optname, + const void* optval, uint32_t optlen); + grpc_error* (*bind)(grpc_custom_socket* socket, const grpc_sockaddr* addr, + size_t len, int flags); + grpc_error* (*listen)(grpc_custom_socket* socket); + void (*accept)(grpc_custom_socket* socket, grpc_custom_socket* client, + grpc_custom_accept_callback cb); +} grpc_socket_vtable; + +/* Internal APIs */ +void grpc_custom_endpoint_init(grpc_socket_vtable* impl); + +void grpc_custom_close_server_callback(grpc_tcp_listener* listener); + +grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket, + grpc_resource_quota* resource_quota, + char* peer_string); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H */ diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index ca0046b83b..205af22531 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -63,7 +63,7 @@ typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type; typedef size_t msg_iovlen_type; #endif -grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); +extern grpc_core::TraceFlag grpc_tcp_trace; namespace { struct grpc_tcp { diff --git a/src/core/lib/iomgr/tcp_server.cc b/src/core/lib/iomgr/tcp_server.cc new file mode 100644 index 0000000000..ea745f266b --- /dev/null +++ b/src/core/lib/iomgr/tcp_server.cc @@ -0,0 +1,73 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/tcp_server.h" + +grpc_tcp_server_vtable* grpc_tcp_server_impl; + +grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + return grpc_tcp_server_impl->create(shutdown_complete, args, server); +} + +void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void* cb_arg) { + grpc_tcp_server_impl->start(server, pollsets, pollset_count, on_accept_cb, + cb_arg); +} + +grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port) { + return grpc_tcp_server_impl->add_port(s, addr, out_port); +} + +unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return grpc_tcp_server_impl->port_fd_count(s, port_index); +} + +int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return grpc_tcp_server_impl->port_fd(s, port_index, fd_index); +} + +grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { + return grpc_tcp_server_impl->ref(s); +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + grpc_tcp_server_impl->shutdown_starting_add(s, shutdown_starting); +} + +void grpc_tcp_server_unref(grpc_tcp_server* s) { + grpc_tcp_server_impl->unref(s); +} + +void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { + grpc_tcp_server_impl->shutdown_listeners(s); +} + +void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl) { + grpc_tcp_server_impl = impl; +} diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h index 965d97407f..8fcbb2f680 100644 --- a/src/core/lib/iomgr/tcp_server.h +++ b/src/core/lib/iomgr/tcp_server.h @@ -45,6 +45,24 @@ typedef void (*grpc_tcp_server_cb)(void* arg, grpc_endpoint* ep, grpc_pollset* accepting_pollset, grpc_tcp_server_acceptor* acceptor); +typedef struct grpc_tcp_server_vtable { + grpc_error* (*create)(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server); + void (*start)(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, grpc_tcp_server_cb on_accept_cb, + void* cb_arg); + grpc_error* (*add_port)(grpc_tcp_server* s, const grpc_resolved_address* addr, + int* out_port); + unsigned (*port_fd_count)(grpc_tcp_server* s, unsigned port_index); + int (*port_fd)(grpc_tcp_server* s, unsigned port_index, unsigned fd_index); + grpc_tcp_server* (*ref)(grpc_tcp_server* s); + void (*shutdown_starting_add)(grpc_tcp_server* s, + grpc_closure* shutdown_starting); + void (*unref)(grpc_tcp_server* s); + void (*shutdown_listeners)(grpc_tcp_server* s); +} grpc_tcp_server_vtable; + /* Create a server, initially not bound to any ports. The caller owns one ref. If shutdown_complete is not NULL, it will be used by grpc_tcp_server_unref() when the ref count reaches zero. */ @@ -97,4 +115,8 @@ void grpc_tcp_server_unref(grpc_tcp_server* s); /* Shutdown the fds of listeners. */ void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s); +void grpc_tcp_server_global_init(); + +void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl); + #endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_H */ diff --git a/src/core/lib/iomgr/tcp_server_custom.cc b/src/core/lib/iomgr/tcp_server_custom.cc new file mode 100644 index 0000000000..be92e61b62 --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_custom.cc @@ -0,0 +1,479 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <assert.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/tcp_server.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +extern grpc_socket_vtable* grpc_custom_socket_vtable; + +/* one listening port */ +struct grpc_tcp_listener { + grpc_tcp_server* server; + unsigned port_index; + int port; + + grpc_custom_socket* socket; + + /* linked list */ + struct grpc_tcp_listener* next; + + bool closed; +}; + +struct grpc_tcp_server { + gpr_refcount refs; + + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void* on_accept_cb_arg; + + int open_ports; + + /* linked list of server ports */ + grpc_tcp_listener* head; + grpc_tcp_listener* tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure* shutdown_complete; + + bool shutdown; + + grpc_resource_quota* resource_quota; +}; + +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); + s->resource_quota = grpc_resource_quota_create(nullptr); + for (size_t i = 0; i < (args == nullptr ? 0 : args->num_args); i++) { + if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_POINTER) { + grpc_resource_quota_unref_internal(s->resource_quota); + s->resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)args->args[i].value.pointer.p); + } else { + grpc_resource_quota_unref_internal(s->resource_quota); + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); + } + } + } + gpr_ref_init(&s->refs, 1); + s->on_accept_cb = nullptr; + s->on_accept_cb_arg = nullptr; + s->open_ports = 0; + s->head = nullptr; + s->tail = nullptr; + s->shutdown_starting.head = nullptr; + s->shutdown_starting.tail = nullptr; + s->shutdown_complete = shutdown_complete; + s->shutdown = false; + *server = s; + return GRPC_ERROR_NONE; +} + +static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_ref(&s->refs); + return s; +} + +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); +} + +static void finish_shutdown(grpc_tcp_server* s) { + GPR_ASSERT(s->shutdown); + if (s->shutdown_complete != nullptr) { + GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); + } + + while (s->head) { + grpc_tcp_listener* sp = s->head; + s->head = sp->next; + sp->next = nullptr; + gpr_free(sp); + } + grpc_resource_quota_unref_internal(s->resource_quota); + gpr_free(s); +} + +static void custom_close_callback(grpc_custom_socket* socket) { + grpc_tcp_listener* sp = socket->listener; + if (sp) { + grpc_core::ExecCtx exec_ctx; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(sp->server); + } + } + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } +} + +void grpc_custom_close_server_callback(grpc_tcp_listener* sp) { + if (sp) { + grpc_core::ExecCtx exec_ctx; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(sp->server); + } + } +} + +static void close_listener(grpc_tcp_listener* sp) { + grpc_custom_socket* socket = sp->socket; + if (!sp->closed) { + sp->closed = true; + grpc_custom_socket_vtable->close(socket, custom_close_callback); + } +} + +static void tcp_server_destroy(grpc_tcp_server* s) { + int immediately_done = 0; + grpc_tcp_listener* sp; + + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + + if (s->open_ports == 0) { + immediately_done = 1; + } + for (sp = s->head; sp; sp = sp->next) { + close_listener(sp); + } + + if (immediately_done) { + finish_shutdown(s); + } +} + +static void tcp_server_unref(grpc_tcp_server* s) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + if (gpr_unref(&s->refs)) { + /* Complete shutdown_starting work before destroying. */ + grpc_core::ExecCtx exec_ctx; + GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); + grpc_core::ExecCtx::Get()->Flush(); + tcp_server_destroy(s); + } +} + +static void finish_accept(grpc_tcp_listener* sp, grpc_custom_socket* socket) { + grpc_tcp_server_acceptor* acceptor = + (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor)); + grpc_endpoint* ep = nullptr; + grpc_resolved_address peer_name; + char* peer_name_string; + grpc_error* err; + + peer_name_string = nullptr; + memset(&peer_name, 0, sizeof(grpc_resolved_address)); + peer_name.len = GRPC_MAX_SOCKADDR_SIZE; + err = grpc_custom_socket_vtable->getpeername( + socket, (grpc_sockaddr*)&peer_name.addr, (int*)&peer_name.len); + if (err == GRPC_ERROR_NONE) { + peer_name_string = grpc_sockaddr_to_uri(&peer_name); + } else { + GRPC_LOG_IF_ERROR("getpeername error", err); + GRPC_ERROR_UNREF(err); + } + if (grpc_tcp_trace.enabled()) { + if (peer_name_string) { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s", + sp->server, peer_name_string); + } else { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection", sp->server); + } + } + ep = custom_tcp_endpoint_create(socket, sp->server->resource_quota, + peer_name_string); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = 0; + sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, nullptr, acceptor); + gpr_free(peer_name_string); +} + +static void custom_accept_callback(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error); + +static void custom_accept_callback(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_tcp_listener* sp = socket->listener; + if (error != GRPC_ERROR_NONE) { + if (!sp->closed) { + gpr_log(GPR_ERROR, "Accept failed: %s", grpc_error_string(error)); + } + gpr_free(client); + GRPC_ERROR_UNREF(error); + return; + } + finish_accept(sp, client); + if (!sp->closed) { + grpc_custom_socket* new_socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + new_socket->endpoint = nullptr; + new_socket->listener = nullptr; + new_socket->connector = nullptr; + new_socket->refs = 1; + grpc_custom_socket_vtable->accept(sp->socket, new_socket, + custom_accept_callback); + } +} + +static grpc_error* add_socket_to_server(grpc_tcp_server* s, + grpc_custom_socket* socket, + const grpc_resolved_address* addr, + unsigned port_index, + grpc_tcp_listener** listener) { + grpc_tcp_listener* sp = nullptr; + int port = -1; + grpc_error* error; + grpc_resolved_address sockname_temp; + + // The last argument to uv_tcp_bind is flags + error = grpc_custom_socket_vtable->bind(socket, (grpc_sockaddr*)addr->addr, + addr->len, 0); + if (error != GRPC_ERROR_NONE) { + return error; + } + + error = grpc_custom_socket_vtable->listen(socket); + if (error != GRPC_ERROR_NONE) { + return error; + } + + sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE; + error = grpc_custom_socket_vtable->getsockname( + socket, (grpc_sockaddr*)&sockname_temp.addr, (int*)&sockname_temp.len); + if (error != GRPC_ERROR_NONE) { + return error; + } + + port = grpc_sockaddr_get_port(&sockname_temp); + + GPR_ASSERT(port >= 0); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = (grpc_tcp_listener*)gpr_zalloc(sizeof(grpc_tcp_listener)); + sp->next = nullptr; + if (s->head == nullptr) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->socket = socket; + sp->port = port; + sp->port_index = port_index; + sp->closed = false; + s->open_ports++; + *listener = sp; + + return GRPC_ERROR_NONE; +} + +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* port) { + // This function is mostly copied from tcp_server_windows.c + grpc_tcp_listener* sp = nullptr; + grpc_custom_socket* socket; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address* allocated_addr = nullptr; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + grpc_error* error = GRPC_ERROR_NONE; + int family; + + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + if (s->tail != nullptr) { + port_index = s->tail->port_index + 1; + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (sp = s->head; sp; sp = sp->next) { + socket = sp->socket; + sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE; + if (nullptr == grpc_custom_socket_vtable->getsockname( + socket, (grpc_sockaddr*)&sockname_temp.addr, + (int*)&sockname_temp.len)) { + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = + (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, port)) { + grpc_sockaddr_make_wildcard6(*port, &wildcard); + + addr = &wildcard; + } + + if (grpc_tcp_trace.enabled()) { + char* port_string; + grpc_sockaddr_to_string(&port_string, addr, 0); + const char* str = grpc_error_string(error); + if (port_string) { + gpr_log(GPR_DEBUG, "SERVER %p add_port %s error=%s", s, port_string, str); + gpr_free(port_string); + } else { + gpr_log(GPR_DEBUG, "SERVER %p add_port error=%s", s, str); + } + } + + family = grpc_sockaddr_get_family(addr); + socket = (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + socket->refs = 1; + socket->endpoint = nullptr; + socket->listener = nullptr; + socket->connector = nullptr; + grpc_custom_socket_vtable->init(socket, family); + + if (error == GRPC_ERROR_NONE) { +#if defined(GPR_LINUX) && defined(SO_REUSEPORT) + if (family == AF_INET || family == AF_INET6) { + int enable = 1; + grpc_custom_socket_vtable->setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, + &enable, sizeof(enable)); + } +#endif /* GPR_LINUX && SO_REUSEPORT */ + error = add_socket_to_server(s, socket, addr, port_index, &sp); + } + gpr_free(allocated_addr); + + if (error != GRPC_ERROR_NONE) { + grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != nullptr); + *port = sp->port; + } + socket->listener = sp; + return error; +} + +static void tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void* cb_arg) { + grpc_tcp_listener* sp; + (void)pollsets; + (void)pollset_count; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "SERVER_START %p", server); + } + GPR_ASSERT(on_accept_cb); + GPR_ASSERT(!server->on_accept_cb); + server->on_accept_cb = on_accept_cb; + server->on_accept_cb_arg = cb_arg; + for (sp = server->head; sp; sp = sp->next) { + grpc_custom_socket* new_socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + new_socket->endpoint = nullptr; + new_socket->listener = nullptr; + new_socket->connector = nullptr; + new_socket->refs = 1; + grpc_custom_socket_vtable->accept(sp->socket, new_socket, + custom_accept_callback); + } +} + +static unsigned tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return 0; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return -1; +} + +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) { + for (grpc_tcp_listener* sp = s->head; sp; sp = sp->next) { + if (!sp->closed) { + sp->closed = true; + grpc_custom_socket_vtable->close(sp->socket, custom_close_callback); + } + } +} + +grpc_tcp_server_vtable custom_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; + +#ifdef GRPC_UV_TEST +grpc_tcp_server_vtable* default_tcp_server_vtable = &custom_tcp_server_vtable; +#endif diff --git a/src/core/lib/iomgr/tcp_server_posix.cc b/src/core/lib/iomgr/tcp_server_posix.cc index a609c09ea7..aba5d6cdb0 100644 --- a/src/core/lib/iomgr/tcp_server_posix.cc +++ b/src/core/lib/iomgr/tcp_server_posix.cc @@ -69,9 +69,9 @@ static void init(void) { #endif } -grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, - const grpc_channel_args* args, - grpc_tcp_server** server) { +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { gpr_once_init(&check_init, init); grpc_tcp_server* s = @@ -392,9 +392,9 @@ static grpc_error* clone_port(grpc_tcp_listener* listener, unsigned count) { return GRPC_ERROR_NONE; } -grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, - const grpc_resolved_address* addr, - int* out_port) { +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port) { grpc_tcp_listener* sp; grpc_resolved_address sockname_temp; grpc_resolved_address addr6_v4mapped; @@ -415,7 +415,7 @@ grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, sockname_temp.len = sizeof(struct sockaddr_storage); if (0 == getsockname(sp->fd, - reinterpret_cast<struct sockaddr*>(&sockname_temp.addr), + reinterpret_cast<grpc_sockaddr*>(&sockname_temp.addr), reinterpret_cast<socklen_t*>(&sockname_temp.len))) { int used_port = grpc_sockaddr_get_port(&sockname_temp); if (used_port > 0) { @@ -458,8 +458,7 @@ static grpc_tcp_listener* get_port_index(grpc_tcp_server* s, return nullptr; } -unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, - unsigned port_index) { +unsigned tcp_server_port_fd_count(grpc_tcp_server* s, unsigned port_index) { unsigned num_fds = 0; gpr_mu_lock(&s->mu); grpc_tcp_listener* sp = get_port_index(s, port_index); @@ -470,8 +469,8 @@ unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, return num_fds; } -int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, - unsigned fd_index) { +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { gpr_mu_lock(&s->mu); grpc_tcp_listener* sp = get_port_index(s, port_index); for (; sp; sp = sp->sibling, --fd_index) { @@ -484,10 +483,10 @@ int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, return -1; } -void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, - size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void* on_accept_cb_arg) { +static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void* on_accept_cb_arg) { size_t i; grpc_tcp_listener* sp; GPR_ASSERT(on_accept_cb); @@ -526,20 +525,20 @@ void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, gpr_mu_unlock(&s->mu); } -grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { +grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { gpr_ref_non_zero(&s->refs); return s; } -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, - grpc_closure* shutdown_starting) { +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { gpr_mu_lock(&s->mu); grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, GRPC_ERROR_NONE); gpr_mu_unlock(&s->mu); } -void grpc_tcp_server_unref(grpc_tcp_server* s) { +static void tcp_server_unref(grpc_tcp_server* s) { if (gpr_unref(&s->refs)) { grpc_tcp_server_shutdown_listeners(s); gpr_mu_lock(&s->mu); @@ -549,7 +548,7 @@ void grpc_tcp_server_unref(grpc_tcp_server* s) { } } -void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) { gpr_mu_lock(&s->mu); s->shutdown_listeners = true; /* shutdown all fd's */ @@ -563,4 +562,14 @@ void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { gpr_mu_unlock(&s->mu); } +grpc_tcp_server_vtable grpc_posix_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; #endif diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc index 846f9cccb7..76d3d62940 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc +++ b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc @@ -171,8 +171,7 @@ grpc_error* grpc_tcp_server_prepare_socket(int fd, if (err != GRPC_ERROR_NONE) goto error; GPR_ASSERT(addr->len < ~(socklen_t)0); - if (bind(fd, - reinterpret_cast<struct sockaddr*>(const_cast<char*>(addr->addr)), + if (bind(fd, reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)), static_cast<socklen_t>(addr->len)) < 0) { err = GRPC_OS_ERROR(errno, "bind"); goto error; @@ -185,7 +184,7 @@ grpc_error* grpc_tcp_server_prepare_socket(int fd, sockname_temp.len = sizeof(struct sockaddr_storage); - if (getsockname(fd, reinterpret_cast<struct sockaddr*>(sockname_temp.addr), + if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr), reinterpret_cast<socklen_t*>(&sockname_temp.len)) < 0) { err = GRPC_OS_ERROR(errno, "getsockname"); goto error; diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc index 308ff0f8a6..29ff9ecda1 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc +++ b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc @@ -68,13 +68,13 @@ static grpc_error* get_unused_port(int* port) { if (dsmode == GRPC_DSMODE_IPV4) { grpc_sockaddr_make_wildcard4(0, &wild); } - if (bind(fd, reinterpret_cast<const struct sockaddr*>(wild.addr), + if (bind(fd, reinterpret_cast<const grpc_sockaddr*>(wild.addr), static_cast<socklen_t>(wild.len)) != 0) { err = GRPC_OS_ERROR(errno, "bind"); close(fd); return err; } - if (getsockname(fd, reinterpret_cast<struct sockaddr*>(wild.addr), + if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(wild.addr), reinterpret_cast<socklen_t*>(&wild.len)) != 0) { err = GRPC_OS_ERROR(errno, "getsockname"); close(fd); @@ -119,9 +119,9 @@ grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s, if (ifa_it->ifa_addr == nullptr) { continue; } else if (ifa_it->ifa_addr->sa_family == AF_INET) { - addr.len = sizeof(struct sockaddr_in); + addr.len = sizeof(grpc_sockaddr_in); } else if (ifa_it->ifa_addr->sa_family == AF_INET6) { - addr.len = sizeof(struct sockaddr_in6); + addr.len = sizeof(grpc_sockaddr_in6); } else { continue; } diff --git a/src/core/lib/iomgr/tcp_server_uv.cc b/src/core/lib/iomgr/tcp_server_uv.cc deleted file mode 100644 index aa423766c7..0000000000 --- a/src/core/lib/iomgr/tcp_server_uv.cc +++ /dev/null @@ -1,473 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include <grpc/support/port_platform.h> - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include <assert.h> -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_server.h" -#include "src/core/lib/iomgr/tcp_uv.h" - -/* one listening port */ -typedef struct grpc_tcp_listener grpc_tcp_listener; -struct grpc_tcp_listener { - uv_tcp_t* handle; - grpc_tcp_server* server; - unsigned port_index; - int port; - /* linked list */ - struct grpc_tcp_listener* next; - - bool closed; - - bool has_pending_connection; -}; - -struct grpc_tcp_server { - gpr_refcount refs; - - /* Called whenever accept() succeeds on a server port. */ - grpc_tcp_server_cb on_accept_cb; - void* on_accept_cb_arg; - - int open_ports; - - /* linked list of server ports */ - grpc_tcp_listener* head; - grpc_tcp_listener* tail; - - /* List of closures passed to shutdown_starting_add(). */ - grpc_closure_list shutdown_starting; - - /* shutdown callback */ - grpc_closure* shutdown_complete; - - bool shutdown; - - grpc_resource_quota* resource_quota; -}; - -grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, - const grpc_channel_args* args, - grpc_tcp_server** server) { - grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); - s->resource_quota = grpc_resource_quota_create(NULL); - for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) { - if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { - if (args->args[i].type == GRPC_ARG_POINTER) { - grpc_resource_quota_unref_internal(s->resource_quota); - s->resource_quota = grpc_resource_quota_ref_internal( - (grpc_resource_quota*)args->args[i].value.pointer.p); - } else { - grpc_resource_quota_unref_internal(s->resource_quota); - gpr_free(s); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); - } - } - } - gpr_ref_init(&s->refs, 1); - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->open_ports = 0; - s->head = NULL; - s->tail = NULL; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - s->shutdown = false; - *server = s; - return GRPC_ERROR_NONE; -} - -grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_ref(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, - grpc_closure* shutdown_starting) { - grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, - GRPC_ERROR_NONE); -} - -static void finish_shutdown(grpc_tcp_server* s) { - GPR_ASSERT(s->shutdown); - if (s->shutdown_complete != NULL) { - GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); - } - - while (s->head) { - grpc_tcp_listener* sp = s->head; - s->head = sp->next; - sp->next = NULL; - gpr_free(sp->handle); - gpr_free(sp); - } - grpc_resource_quota_unref_internal(s->resource_quota); - gpr_free(s); -} - -static void handle_close_callback(uv_handle_t* handle) { - grpc_tcp_listener* sp = (grpc_tcp_listener*)handle->data; - grpc_core::ExecCtx exec_ctx; - sp->server->open_ports--; - if (sp->server->open_ports == 0 && sp->server->shutdown) { - finish_shutdown(sp->server); - } -} - -static void close_listener(grpc_tcp_listener* sp) { - if (!sp->closed) { - sp->closed = true; - uv_close((uv_handle_t*)sp->handle, handle_close_callback); - } -} - -static void tcp_server_destroy(grpc_tcp_server* s) { - int immediately_done = 0; - grpc_tcp_listener* sp; - - GPR_ASSERT(!s->shutdown); - s->shutdown = true; - - if (s->open_ports == 0) { - immediately_done = 1; - } - for (sp = s->head; sp; sp = sp->next) { - close_listener(sp); - } - - if (immediately_done) { - finish_shutdown(s); - } -} - -void grpc_tcp_server_unref(grpc_tcp_server* s) { - GRPC_UV_ASSERT_SAME_THREAD(); - if (gpr_unref(&s->refs)) { - /* Complete shutdown_starting work before destroying. */ - grpc_core::ExecCtx exec_ctx; - GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); - grpc_core::ExecCtx::Get()->Flush(); - tcp_server_destroy(s); - } -} - -static void finish_accept(grpc_tcp_listener* sp) { - grpc_tcp_server_acceptor* acceptor = - (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor)); - uv_tcp_t* client = NULL; - grpc_endpoint* ep = NULL; - grpc_resolved_address peer_name; - char* peer_name_string; - int err; - uv_tcp_t* server = sp->handle; - - client = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); - uv_tcp_init(uv_default_loop(), client); - // UV documentation says this is guaranteed to succeed - uv_accept((uv_stream_t*)server, (uv_stream_t*)client); - peer_name_string = NULL; - memset(&peer_name, 0, sizeof(grpc_resolved_address)); - peer_name.len = sizeof(struct sockaddr_storage); - err = uv_tcp_getpeername(client, (struct sockaddr*)&peer_name.addr, - (int*)&peer_name.len); - if (err == 0) { - peer_name_string = grpc_sockaddr_to_uri(&peer_name); - } else { - gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(err)); - } - if (grpc_tcp_trace.enabled()) { - if (peer_name_string) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s", - sp->server, peer_name_string); - } else { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection", sp->server); - } - } - ep = grpc_tcp_create(client, sp->server->resource_quota, peer_name_string); - acceptor->from_server = sp->server; - acceptor->port_index = sp->port_index; - acceptor->fd_index = 0; - sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, NULL, acceptor); - gpr_free(peer_name_string); -} - -static void on_connect(uv_stream_t* server, int status) { - grpc_tcp_listener* sp = (grpc_tcp_listener*)server->data; - grpc_core::ExecCtx exec_ctx; - - if (status < 0) { - switch (status) { - case UV_EINTR: - case UV_EAGAIN: - return; - default: - close_listener(sp); - return; - } - } - - GPR_ASSERT(!sp->has_pending_connection); - - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p incoming connection", sp->server); - } - - // Create acceptor. - if (sp->server->on_accept_cb) { - finish_accept(sp); - } else { - sp->has_pending_connection = true; - } -} - -static grpc_error* add_addr_to_server(grpc_tcp_server* s, - const grpc_resolved_address* addr, - unsigned port_index, - grpc_tcp_listener** listener) { - grpc_tcp_listener* sp = NULL; - int port = -1; - int status; - grpc_error* error; - grpc_resolved_address sockname_temp; - uv_tcp_t* handle = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); - int family = grpc_sockaddr_get_family(addr); - - status = uv_tcp_init_ex(uv_default_loop(), handle, (unsigned int)family); -#if defined(GPR_LINUX) && defined(SO_REUSEPORT) - if (family == AF_INET || family == AF_INET6) { - int fd; - uv_fileno((uv_handle_t*)handle, &fd); - int enable = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)); - } -#endif /* GPR_LINUX && SO_REUSEPORT */ - - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to initialize UV tcp handle"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - // The last argument to uv_tcp_bind is flags - status = uv_tcp_bind(handle, (struct sockaddr*)addr->addr, 0); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to bind to port"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - status = uv_listen((uv_stream_t*)handle, SOMAXCONN, on_connect); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to listen to port"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - sockname_temp.len = (int)sizeof(struct sockaddr_storage); - status = uv_tcp_getsockname(handle, (struct sockaddr*)&sockname_temp.addr, - (int*)&sockname_temp.len); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getsockname failed"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - port = grpc_sockaddr_get_port(&sockname_temp); - - GPR_ASSERT(port >= 0); - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = (grpc_tcp_listener*)gpr_zalloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->handle = handle; - sp->port = port; - sp->port_index = port_index; - sp->closed = false; - handle->data = sp; - s->open_ports++; - GPR_ASSERT(sp->handle); - *listener = sp; - - return GRPC_ERROR_NONE; -} - -static grpc_error* add_wildcard_addrs_to_server(grpc_tcp_server* s, - unsigned port_index, - int requested_port, - grpc_tcp_listener** listener) { - grpc_resolved_address wild4; - grpc_resolved_address wild6; - grpc_tcp_listener* sp = nullptr; - grpc_tcp_listener* sp2 = nullptr; - grpc_error* v6_err = GRPC_ERROR_NONE; - grpc_error* v4_err = GRPC_ERROR_NONE; - - grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6); - /* Try listening on IPv6 first. */ - if ((v6_err = add_addr_to_server(s, &wild6, port_index, &sp)) == - GRPC_ERROR_NONE) { - *listener = sp; - return GRPC_ERROR_NONE; - } - - if ((v4_err = add_addr_to_server(s, &wild4, port_index, &sp2)) == - GRPC_ERROR_NONE) { - *listener = sp2; - return GRPC_ERROR_NONE; - } - - grpc_error* root_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to add any wildcard listeners"); - root_err = grpc_error_add_child(root_err, v6_err); - root_err = grpc_error_add_child(root_err, v4_err); - return root_err; -} - -grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, - const grpc_resolved_address* addr, - int* port) { - // This function is mostly copied from tcp_server_windows.c - grpc_tcp_listener* sp = NULL; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address* allocated_addr = NULL; - grpc_resolved_address sockname_temp; - unsigned port_index = 0; - grpc_error* error = GRPC_ERROR_NONE; - - GRPC_UV_ASSERT_SAME_THREAD(); - - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - - /* Check if this is a wildcard port, and if so, try to keep the port the same - as some previously created listener. */ - if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { - sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == uv_tcp_getsockname(sp->handle, - (struct sockaddr*)&sockname_temp.addr, - (int*)&sockname_temp.len)) { - *port = grpc_sockaddr_get_port(&sockname_temp); - if (*port > 0) { - allocated_addr = - (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address)); - memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); - grpc_sockaddr_set_port(allocated_addr, *port); - addr = allocated_addr; - break; - } - } - } - } - - /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ - if (grpc_sockaddr_is_wildcard(addr, port)) { - error = add_wildcard_addrs_to_server(s, port_index, *port, &sp); - } else { - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - error = add_addr_to_server(s, addr, port_index, &sp); - } - - gpr_free(allocated_addr); - - if (grpc_tcp_trace.enabled()) { - char* port_string; - grpc_sockaddr_to_string(&port_string, addr, 0); - const char* str = grpc_error_string(error); - if (port_string) { - gpr_log(GPR_DEBUG, "SERVER %p add_port %s error=%s", s, port_string, str); - gpr_free(port_string); - } else { - gpr_log(GPR_DEBUG, "SERVER %p add_port error=%s", s, str); - } - } - - if (error != GRPC_ERROR_NONE) { - grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to add port to server", &error, 1); - GRPC_ERROR_UNREF(error); - error = error_out; - *port = -1; - } else { - GPR_ASSERT(sp != NULL); - *port = sp->port; - } - return error; -} - -void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, - size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, void* cb_arg) { - grpc_tcp_listener* sp; - (void)pollsets; - (void)pollset_count; - GRPC_UV_ASSERT_SAME_THREAD(); - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "SERVER_START %p", server); - } - GPR_ASSERT(on_accept_cb); - GPR_ASSERT(!server->on_accept_cb); - server->on_accept_cb = on_accept_cb; - server->on_accept_cb_arg = cb_arg; - for (sp = server->head; sp; sp = sp->next) { - if (sp->has_pending_connection) { - finish_accept(sp); - sp->has_pending_connection = false; - } - } -} - -void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) {} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_server_windows.cc b/src/core/lib/iomgr/tcp_server_windows.cc index 6d19c1c4d7..77f3811dca 100644 --- a/src/core/lib/iomgr/tcp_server_windows.cc +++ b/src/core/lib/iomgr/tcp_server_windows.cc @@ -50,7 +50,7 @@ typedef struct grpc_tcp_listener grpc_tcp_listener; struct grpc_tcp_listener { /* This seemingly magic number comes from AcceptEx's documentation. each address buffer needs to have at least 16 more bytes at their end. */ - uint8_t addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; + uint8_t addresses[(sizeof(grpc_sockaddr_in6) + 16) * 2]; /* This will hold the socket for the next accept. */ SOCKET new_socket; /* The listener winsocket. */ @@ -96,9 +96,9 @@ struct grpc_tcp_server { /* Public function. Allocates the proper data structures to hold a grpc_tcp_server. */ -grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, - const grpc_channel_args* args, - grpc_tcp_server** server) { +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); s->channel_args = grpc_channel_args_copy(args); gpr_ref_init(&s->refs, 1); @@ -142,13 +142,13 @@ static void finish_shutdown_locked(grpc_tcp_server* s) { GRPC_ERROR_NONE); } -grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { +static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { gpr_ref_non_zero(&s->refs); return s; } -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, - grpc_closure* shutdown_starting) { +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { gpr_mu_lock(&s->mu); grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, GRPC_ERROR_NONE); @@ -172,7 +172,7 @@ static void tcp_server_destroy(grpc_tcp_server* s) { gpr_mu_unlock(&s->mu); } -void grpc_tcp_server_unref(grpc_tcp_server* s) { +static void tcp_server_unref(grpc_tcp_server* s) { if (gpr_unref(&s->refs)) { grpc_tcp_server_shutdown_listeners(s); gpr_mu_lock(&s->mu); @@ -195,7 +195,7 @@ static grpc_error* prepare_socket(SOCKET sock, goto failure; } - if (bind(sock, (const struct sockaddr*)addr->addr, (int)addr->len) == + if (bind(sock, (const grpc_sockaddr*)addr->addr, (int)addr->len) == SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); goto failure; @@ -207,7 +207,7 @@ static grpc_error* prepare_socket(SOCKET sock, } sockname_temp_len = sizeof(struct sockaddr_storage); - if (getsockname(sock, (struct sockaddr*)sockname_temp.addr, + if (getsockname(sock, (grpc_sockaddr*)sockname_temp.addr, &sockname_temp_len) == SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); goto failure; @@ -245,7 +245,7 @@ static void decrement_active_ports_and_notify_locked(grpc_tcp_listener* sp) { static grpc_error* start_accept_locked(grpc_tcp_listener* port) { SOCKET sock = INVALID_SOCKET; BOOL success; - DWORD addrlen = sizeof(struct sockaddr_in6) + 16; + DWORD addrlen = sizeof(grpc_sockaddr_in6) + 16; DWORD bytes_received = 0; grpc_error* error = GRPC_ERROR_NONE; @@ -343,7 +343,7 @@ static void on_accept(void* arg, grpc_error* error) { gpr_free(utf8_message); } int peer_name_len = (int)peer_name.len; - err = getpeername(sock, (struct sockaddr*)peer_name.addr, &peer_name_len); + err = getpeername(sock, (grpc_sockaddr*)peer_name.addr, &peer_name_len); peer_name.len = (size_t)peer_name_len; if (!err) { peer_name_string = grpc_sockaddr_to_uri(&peer_name); @@ -442,9 +442,9 @@ static grpc_error* add_socket_to_server(grpc_tcp_server* s, SOCKET sock, return GRPC_ERROR_NONE; } -grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, - const grpc_resolved_address* addr, - int* port) { +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* port) { grpc_tcp_listener* sp = NULL; SOCKET sock; grpc_resolved_address addr6_v4mapped; @@ -464,7 +464,7 @@ grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, for (sp = s->head; sp; sp = sp->next) { int sockname_temp_len = sizeof(struct sockaddr_storage); if (0 == getsockname(sp->socket->socket, - (struct sockaddr*)sockname_temp.addr, + (grpc_sockaddr*)sockname_temp.addr, &sockname_temp_len)) { sockname_temp.len = (size_t)sockname_temp_len; *port = grpc_sockaddr_get_port(&sockname_temp); @@ -516,10 +516,10 @@ done: return error; } -void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, - size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void* on_accept_cb_arg) { +static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void* on_accept_cb_arg) { grpc_tcp_listener* sp; GPR_ASSERT(on_accept_cb); gpr_mu_lock(&s->mu); @@ -534,6 +534,26 @@ void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, gpr_mu_unlock(&s->mu); } -void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) {} +static unsigned tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return 0; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return -1; +} +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {} + +grpc_tcp_server_vtable grpc_windows_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_uv.cc b/src/core/lib/iomgr/tcp_uv.cc index 6db3217d6e..5e3166926b 100644 --- a/src/core/lib/iomgr/tcp_uv.cc +++ b/src/core/lib/iomgr/tcp_uv.cc @@ -21,7 +21,6 @@ #include "src/core/lib/iomgr/port.h" #ifdef GRPC_UV - #include <limits.h> #include <string.h> @@ -33,393 +32,393 @@ #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/iomgr_custom.h" #include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" #include "src/core/lib/iomgr/resource_quota.h" -#include "src/core/lib/iomgr/tcp_uv.h" +#include "src/core/lib/iomgr/tcp_custom.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" -grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); +#include <uv.h> -typedef struct { - grpc_endpoint base; - gpr_refcount refcount; +#define IGNORE_CONST(addr) ((grpc_sockaddr*)(uintptr_t)(addr)) +typedef struct uv_socket_t { + uv_connect_t connect_req; uv_write_t write_req; uv_shutdown_t shutdown_req; - uv_tcp_t* handle; - - grpc_closure* read_cb; - grpc_closure* write_cb; - - grpc_slice_buffer* read_slices; - grpc_slice_buffer* write_slices; uv_buf_t* write_buffers; - grpc_resource_user* resource_user; - grpc_resource_user_slice_allocator slice_allocator; - - bool shutting_down; + char* read_buf; + size_t read_len; - char* peer_string; - grpc_pollset* pollset; -} grpc_tcp; + bool pending_connection; + grpc_custom_socket* accept_socket; + grpc_error* accept_error; -static grpc_error* tcp_annotate_error(grpc_error* src_error, grpc_tcp* tcp) { - return grpc_error_set_str( - grpc_error_set_int( - src_error, - /* All tcp errors are marked with UNAVAILABLE so that application may - * choose to retry. */ - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), - GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(tcp->peer_string)); -} + grpc_custom_connect_callback connect_cb; + grpc_custom_write_callback write_cb; + grpc_custom_read_callback read_cb; + grpc_custom_accept_callback accept_cb; + grpc_custom_close_callback close_cb; -static void tcp_free(grpc_tcp* tcp) { - grpc_resource_user_unref(tcp->resource_user); - gpr_free(tcp->handle); - gpr_free(tcp->peer_string); - gpr_free(tcp); -} - -#ifndef NDEBUG -#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) -static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file, - int line) { - if (grpc_tcp_trace.enabled()) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val - 1); - } - if (gpr_unref(&tcp->refcount)) { - tcp_free(tcp); - } -} +} uv_socket_t; -static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file, - int line) { - if (grpc_tcp_trace.enabled()) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val + 1); - } - gpr_ref(&tcp->refcount); -} -#else -#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) -#define TCP_REF(tcp, reason) tcp_ref((tcp)) -static void tcp_unref(grpc_tcp* tcp) { - if (gpr_unref(&tcp->refcount)) { - tcp_free(tcp); +static grpc_error* tcp_error_create(const char* desc, int status) { + if (status == 0) { + return GRPC_ERROR_NONE; } + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc); + /* All tcp errors are marked with UNAVAILABLE so that application may + * choose to retry. */ + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + return grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); } -static void tcp_ref(grpc_tcp* tcp) { gpr_ref(&tcp->refcount); } -#endif - -static void uv_close_callback(uv_handle_t* handle) { - grpc_core::ExecCtx exec_ctx; - grpc_tcp* tcp = (grpc_tcp*)handle->data; - TCP_UNREF(tcp, "destroy"); +static void uv_socket_destroy(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + gpr_free(uv_socket->handle); + gpr_free(uv_socket); } static void alloc_uv_buf(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - grpc_core::ExecCtx exec_ctx; - grpc_tcp* tcp = (grpc_tcp*)handle->data; + uv_socket_t* uv_socket = + (uv_socket_t*)((grpc_custom_socket*)handle->data)->impl; (void)suggested_size; - /* Before calling uv_read_start, we allocate a buffer with exactly one slice - * to tcp->read_slices and wait for the callback indicating that the - * allocation was successful. So slices[0] should always exist here */ - buf->base = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]); - buf->len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]); -} - -static void call_read_cb(grpc_tcp* tcp, grpc_error* error) { - grpc_closure* cb = tcp->read_cb; - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg); - size_t i; - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "read: error=%s", str); - - for (i = 0; i < tcp->read_slices->count; i++) { - char* dump = grpc_dump_slice(tcp->read_slices->slices[i], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); - gpr_free(dump); - } - } - tcp->read_slices = NULL; - tcp->read_cb = NULL; - GRPC_CLOSURE_RUN(cb, error); + buf->base = uv_socket->read_buf; + buf->len = uv_socket->read_len; } -static void read_callback(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf) { - grpc_error* error; - grpc_core::ExecCtx exec_ctx; - grpc_tcp* tcp = (grpc_tcp*)stream->data; - grpc_slice_buffer garbage; +static void uv_read_callback(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + grpc_error* error = GRPC_ERROR_NONE; if (nread == 0) { // Nothing happened. Wait for the next callback return; } - TCP_UNREF(tcp, "read"); // TODO(murgatroid99): figure out what the return value here means uv_read_stop(stream); if (nread == UV_EOF) { - error = - tcp_annotate_error(GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"), tcp); - grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); - } else if (nread > 0) { - // Successful read - error = GRPC_ERROR_NONE; - if ((size_t)nread < tcp->read_slices->length) { - /* TODO(murgatroid99): Instead of discarding the unused part of the read - * buffer, reuse it as the next read buffer. */ - grpc_slice_buffer_init(&garbage); - grpc_slice_buffer_trim_end( - tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage); - grpc_slice_buffer_reset_and_unref_internal(&garbage); - } - } else { - // nread < 0: Error - error = tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed"), tcp); - grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } else if (nread < 0) { + error = tcp_error_create("TCP Read failed", nread); } - call_read_cb(tcp, error); + grpc_custom_socket* socket = (grpc_custom_socket*)stream->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->read_cb(socket, (size_t)nread, error); } -static void tcp_read_allocation_done(void* tcpp, grpc_error* error) { - int status; - grpc_tcp* tcp = (grpc_tcp*)tcpp; - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp, - grpc_error_string(error)); - } - if (error == GRPC_ERROR_NONE) { - status = - uv_read_start((uv_stream_t*)tcp->handle, alloc_uv_buf, read_callback); - if (status != 0) { - error = tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed at start"), - tcp); - error = grpc_error_set_str( - error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - } - } - if (error != GRPC_ERROR_NONE) { - grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); - call_read_cb(tcp, GRPC_ERROR_REF(error)); - TCP_UNREF(tcp, "read"); +static void uv_close_callback(uv_handle_t* handle) { + grpc_custom_socket* socket = (grpc_custom_socket*)handle->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + if (uv_socket->accept_socket) { + uv_socket->accept_cb(socket, uv_socket->accept_socket, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket closed")); } - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str); + uv_socket->close_cb(socket); +} + +static void uv_socket_read(grpc_custom_socket* socket, char* buffer, + size_t length, grpc_custom_read_callback read_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status; + grpc_error* error; + uv_socket->read_cb = read_cb; + uv_socket->read_buf = buffer; + uv_socket->read_len = length; + // TODO(murgatroid99): figure out what the return value here means + status = + uv_read_start((uv_stream_t*)uv_socket->handle, (uv_alloc_cb)alloc_uv_buf, + (uv_read_cb)uv_read_callback); + if (status != 0) { + error = tcp_error_create("TCP Read failed at start", status); + uv_socket->read_cb(socket, 0, error); } } -static void uv_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices, - grpc_closure* cb) { - grpc_tcp* tcp = (grpc_tcp*)ep; - GRPC_UV_ASSERT_SAME_THREAD(); - GPR_ASSERT(tcp->read_cb == NULL); - tcp->read_cb = cb; - tcp->read_slices = read_slices; - grpc_slice_buffer_reset_and_unref_internal(read_slices); - TCP_REF(tcp, "read"); - grpc_resource_user_alloc_slices(&tcp->slice_allocator, - GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, - tcp->read_slices); +static void uv_write_callback(uv_write_t* req, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)req->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + gpr_free(uv_socket->write_buffers); + uv_socket->write_cb(socket, tcp_error_create("TCP Write failed", status)); } -static void write_callback(uv_write_t* req, int status) { - grpc_tcp* tcp = (grpc_tcp*)req->data; - grpc_error* error; - grpc_core::ExecCtx exec_ctx; - grpc_closure* cb = tcp->write_cb; - tcp->write_cb = NULL; - TCP_UNREF(tcp, "write"); - if (status == 0) { - error = GRPC_ERROR_NONE; - } else { - error = tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Write failed"), tcp); - } - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str); +void uv_socket_write(grpc_custom_socket* socket, + grpc_slice_buffer* write_slices, + grpc_custom_write_callback write_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->write_cb = write_cb; + uv_buf_t* uv_buffers; + uv_write_t* write_req; + + uv_buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * write_slices->count); + for (size_t i = 0; i < write_slices->count; i++) { + uv_buffers[i].base = (char*)GRPC_SLICE_START_PTR(write_slices->slices[i]); + uv_buffers[i].len = GRPC_SLICE_LENGTH(write_slices->slices[i]); } - gpr_free(tcp->write_buffers); - GRPC_CLOSURE_SCHED(cb, error); + + uv_socket->write_buffers = uv_buffers; + write_req = &uv_socket->write_req; + write_req->data = socket; + // TODO(murgatroid99): figure out what the return value here means + uv_write(write_req, (uv_stream_t*)uv_socket->handle, uv_buffers, + write_slices->count, uv_write_callback); } -static void uv_endpoint_write(grpc_endpoint* ep, - grpc_slice_buffer* write_slices, - grpc_closure* cb) { - grpc_tcp* tcp = (grpc_tcp*)ep; - uv_buf_t* buffers; - unsigned int buffer_count; - unsigned int i; - grpc_slice* slice; - uv_write_t* write_req; - GRPC_UV_ASSERT_SAME_THREAD(); +static void shutdown_callback(uv_shutdown_t* req, int status) {} - if (grpc_tcp_trace.enabled()) { - size_t j; +static void uv_socket_shutdown(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_shutdown_t* req = &uv_socket->shutdown_req; + uv_shutdown(req, (uv_stream_t*)uv_socket->handle, shutdown_callback); +} - for (j = 0; j < write_slices->count; j++) { - char* data = grpc_dump_slice(write_slices->slices[j], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); - gpr_free(data); - } +static void uv_socket_close(grpc_custom_socket* socket, + grpc_custom_close_callback close_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->close_cb = close_cb; + uv_close((uv_handle_t*)uv_socket->handle, uv_close_callback); +} + +static grpc_error* uv_socket_init_helper(uv_socket_t* uv_socket, int domain) { + uv_tcp_t* tcp = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); + uv_socket->handle = tcp; + int status = uv_tcp_init_ex(uv_default_loop(), tcp, (unsigned int)domain); + if (status != 0) { + return tcp_error_create("Failed to initialize UV tcp handle", status); } + uv_socket->write_buffers = nullptr; + uv_socket->read_len = 0; + uv_tcp_nodelay(uv_socket->handle, 1); + uv_socket->pending_connection = false; + uv_socket->accept_socket = nullptr; + uv_socket->accept_error = GRPC_ERROR_NONE; + return GRPC_ERROR_NONE; +} - if (tcp->shutting_down) { - GRPC_CLOSURE_SCHED(cb, - tcp_annotate_error(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "TCP socket is shutting down"), - tcp)); - return; +static grpc_error* uv_socket_init(grpc_custom_socket* socket, int domain) { + uv_socket_t* uv_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t)); + grpc_error* error = uv_socket_init_helper(uv_socket, domain); + if (error != GRPC_ERROR_NONE) { + return error; } + uv_socket->handle->data = socket; + socket->impl = uv_socket; + return GRPC_ERROR_NONE; +} + +static grpc_error* uv_socket_getpeername(grpc_custom_socket* socket, + const grpc_sockaddr* addr, + int* addr_len) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int err = uv_tcp_getpeername(uv_socket->handle, + (struct sockaddr*)IGNORE_CONST(addr), addr_len); + return tcp_error_create("getpeername failed", err); +} + +static grpc_error* uv_socket_getsockname(grpc_custom_socket* socket, + const grpc_sockaddr* addr, + int* addr_len) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int err = uv_tcp_getsockname(uv_socket->handle, + (struct sockaddr*)IGNORE_CONST(addr), addr_len); + return tcp_error_create("getsockname failed", err); +} - GPR_ASSERT(tcp->write_cb == NULL); - tcp->write_slices = write_slices; - GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); - if (tcp->write_slices->count == 0) { - // No slices means we don't have to do anything, - // and libuv doesn't like empty writes - GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_NONE); +static void accept_new_connection(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + if (!uv_socket->pending_connection || !uv_socket->accept_socket) { return; } + grpc_custom_socket* new_socket = uv_socket->accept_socket; + grpc_error* error = uv_socket->accept_error; + uv_socket->accept_socket = nullptr; + uv_socket->accept_error = GRPC_ERROR_NONE; + uv_socket->pending_connection = false; + if (uv_socket->accept_error != GRPC_ERROR_NONE) { + uv_stream_t dummy_handle; + uv_accept((uv_stream_t*)uv_socket->handle, &dummy_handle); + uv_socket->accept_cb(socket, new_socket, error); + } else { + uv_socket_t* uv_new_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t)); + uv_socket_init_helper(uv_new_socket, AF_UNSPEC); + // UV documentation says this is guaranteed to succeed + GPR_ASSERT(uv_accept((uv_stream_t*)uv_socket->handle, + (uv_stream_t*)uv_new_socket->handle) == 0); + new_socket->impl = uv_new_socket; + uv_new_socket->handle->data = new_socket; + uv_socket->accept_cb(socket, new_socket, error); + } +} - tcp->write_cb = cb; - buffer_count = (unsigned int)tcp->write_slices->count; - buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * buffer_count); - for (i = 0; i < buffer_count; i++) { - slice = &tcp->write_slices->slices[i]; - buffers[i].base = (char*)GRPC_SLICE_START_PTR(*slice); - buffers[i].len = GRPC_SLICE_LENGTH(*slice); +static void uv_on_connect(uv_stream_t* server, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)server->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + GPR_ASSERT(!uv_socket->pending_connection); + uv_socket->pending_connection = true; + if (status < 0) { + switch (status) { + case UV_EINTR: + case UV_EAGAIN: + return; + default: + uv_socket->accept_error = tcp_error_create("accept failed", status); + } } - tcp->write_buffers = buffers; - write_req = &tcp->write_req; - write_req->data = tcp; - TCP_REF(tcp, "write"); - // TODO(murgatroid99): figure out what the return value here means - uv_write(write_req, (uv_stream_t*)tcp->handle, buffers, buffer_count, - write_callback); + accept_new_connection(socket); } -static void uv_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) { - // No-op. We're ignoring pollsets currently - (void)ep; - (void)pollset; - grpc_tcp* tcp = (grpc_tcp*)ep; - tcp->pollset = pollset; +void uv_socket_accept(grpc_custom_socket* socket, + grpc_custom_socket* new_socket, + grpc_custom_accept_callback accept_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->accept_cb = accept_cb; + GPR_ASSERT(uv_socket->accept_socket == nullptr); + uv_socket->accept_socket = new_socket; + accept_new_connection(socket); } -static void uv_add_to_pollset_set(grpc_endpoint* ep, - grpc_pollset_set* pollset) { - // No-op. We're ignoring pollsets currently - (void)ep; - (void)pollset; +static grpc_error* uv_socket_bind(grpc_custom_socket* socket, + const grpc_sockaddr* addr, size_t len, + int flags) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status = + uv_tcp_bind((uv_tcp_t*)uv_socket->handle, (struct sockaddr*)addr, 0); + return tcp_error_create("Failed to bind to port", status); } -static void uv_delete_from_pollset_set(grpc_endpoint* ep, - grpc_pollset_set* pollset) { - // No-op. We're ignoring pollsets currently - (void)ep; - (void)pollset; +static grpc_error* uv_socket_listen(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status = + uv_listen((uv_stream_t*)uv_socket->handle, SOMAXCONN, uv_on_connect); + return tcp_error_create("Failed to listen to port", status); } -static void shutdown_callback(uv_shutdown_t* req, int status) {} +static grpc_error* uv_socket_setsockopt(grpc_custom_socket* socket, int level, + int option_name, const void* optval, + socklen_t option_len) { + int fd; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_fileno((uv_handle_t*)uv_socket->handle, &fd); + // TODO Handle error here. Also, does this work on windows?? + setsockopt(fd, level, option_name, &optval, (socklen_t)option_len); + return GRPC_ERROR_NONE; +} -static void uv_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) { - grpc_tcp* tcp = (grpc_tcp*)ep; - if (!tcp->shutting_down) { - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(why); - gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->handle, str); - } - tcp->shutting_down = true; - uv_shutdown_t* req = &tcp->shutdown_req; - uv_shutdown(req, (uv_stream_t*)tcp->handle, shutdown_callback); - grpc_resource_user_shutdown(tcp->resource_user); +static void uv_tc_on_connect(uv_connect_t* req, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)req->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + grpc_error* error; + if (status == UV_ECANCELED) { + // This should only happen if the handle is already closed + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timeout occurred"); + } else { + error = tcp_error_create("Failed to connect to remote host", status); } - GRPC_ERROR_UNREF(why); + uv_socket->connect_cb(socket, error); } -static void uv_destroy(grpc_endpoint* ep) { - grpc_network_status_unregister_endpoint(ep); - grpc_tcp* tcp = (grpc_tcp*)ep; - uv_close((uv_handle_t*)tcp->handle, uv_close_callback); +static void uv_socket_connect(grpc_custom_socket* socket, + const grpc_sockaddr* addr, size_t len, + grpc_custom_connect_callback connect_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->connect_cb = connect_cb; + uv_socket->connect_req.data = socket; + int status = uv_tcp_connect(&uv_socket->connect_req, uv_socket->handle, + (struct sockaddr*)addr, uv_tc_on_connect); + if (status != 0) { + // The callback will not be called + uv_socket->connect_cb(socket, tcp_error_create("connect failed", status)); + } +} + +static grpc_resolved_addresses* handle_addrinfo_result( + struct addrinfo* result) { + struct addrinfo* resp; + struct addrinfo* prev; + size_t i; + grpc_resolved_addresses* addresses = + (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses)); + addresses->naddrs = 0; + for (resp = result; resp != nullptr; resp = resp->ai_next) { + addresses->naddrs++; + } + addresses->addrs = (grpc_resolved_address*)gpr_malloc( + sizeof(grpc_resolved_address) * addresses->naddrs); + i = 0; + resp = result; + while (resp != nullptr) { + memcpy(&addresses->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + addresses->addrs[i].len = resp->ai_addrlen; + i++; + prev = resp; + resp = resp->ai_next; + gpr_free(prev); + } + return addresses; } -static char* uv_get_peer(grpc_endpoint* ep) { - grpc_tcp* tcp = (grpc_tcp*)ep; - return gpr_strdup(tcp->peer_string); +static void uv_resolve_callback(uv_getaddrinfo_t* req, int status, + struct addrinfo* res) { + grpc_custom_resolver* r = (grpc_custom_resolver*)req->data; + gpr_free(req); + grpc_resolved_addresses* result = nullptr; + if (status == 0) { + result = handle_addrinfo_result(res); + } + grpc_custom_resolve_callback(r, result, + tcp_error_create("getaddrinfo failed", status)); } -static grpc_resource_user* uv_get_resource_user(grpc_endpoint* ep) { - grpc_tcp* tcp = (grpc_tcp*)ep; - return tcp->resource_user; +static grpc_error* uv_resolve(char* host, char* port, + grpc_resolved_addresses** result) { + int status; + uv_getaddrinfo_t req; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + status = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); + if (status != 0) { + *result = nullptr; + } else { + *result = handle_addrinfo_result(req.addrinfo); + } + return tcp_error_create("getaddrinfo failed", status); } -static int uv_get_fd(grpc_endpoint* ep) { return -1; } - -static grpc_endpoint_vtable vtable = {uv_endpoint_read, - uv_endpoint_write, - uv_add_to_pollset, - uv_add_to_pollset_set, - uv_delete_from_pollset_set, - uv_endpoint_shutdown, - uv_destroy, - uv_get_resource_user, - uv_get_peer, - uv_get_fd}; - -grpc_endpoint* grpc_tcp_create(uv_tcp_t* handle, - grpc_resource_quota* resource_quota, - char* peer_string) { - grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp)); - grpc_core::ExecCtx exec_ctx; - - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); +static void uv_resolve_async(grpc_custom_resolver* r, char* host, char* port) { + int status; + uv_getaddrinfo_t* req = + (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); + req->data = r; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = GRPC_AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = GRPC_SOCK_STREAM; /* stream socket */ + hints.ai_flags = GRPC_AI_PASSIVE; /* for wildcard IP address */ + status = uv_getaddrinfo(uv_default_loop(), req, uv_resolve_callback, host, + port, &hints); + if (status != 0) { + gpr_free(req); + grpc_error* error = tcp_error_create("getaddrinfo failed", status); + grpc_custom_resolve_callback(r, NULL, error); } +} - /* Disable Nagle's Algorithm */ - uv_tcp_nodelay(handle, 1); - - memset(tcp, 0, sizeof(grpc_tcp)); - tcp->base.vtable = &vtable; - tcp->handle = handle; - handle->data = tcp; - gpr_ref_init(&tcp->refcount, 1); - tcp->peer_string = gpr_strdup(peer_string); - tcp->shutting_down = false; - tcp->read_slices = NULL; - tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); - grpc_resource_user_slice_allocator_init( - &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); - /* Tell network status tracking code about the new endpoint */ - grpc_network_status_register_endpoint(&tcp->base); - -#ifndef GRPC_UV_TCP_HOLD_LOOP - uv_unref((uv_handle_t*)handle); -#endif +grpc_custom_resolver_vtable uv_resolver_vtable = {uv_resolve, uv_resolve_async}; - return &tcp->base; -} +grpc_socket_vtable grpc_uv_socket_vtable = { + uv_socket_init, uv_socket_connect, uv_socket_destroy, + uv_socket_shutdown, uv_socket_close, uv_socket_write, + uv_socket_read, uv_socket_getpeername, uv_socket_getsockname, + uv_socket_setsockopt, uv_socket_bind, uv_socket_listen, + uv_socket_accept}; -#endif /* GRPC_UV */ +#endif diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h deleted file mode 100644 index 6b1a6f77c2..0000000000 --- a/src/core/lib/iomgr/tcp_uv.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef GRPC_CORE_LIB_IOMGR_TCP_UV_H -#define GRPC_CORE_LIB_IOMGR_TCP_UV_H -/* - Low level TCP "bottom half" implementation, for use by transports built on - top of a TCP connection. - - Note that this file does not (yet) include APIs for creating the socket in - the first place. - - All calls passing slice transfer ownership of a slice refcount unless - otherwise specified. -*/ - -#include <grpc/support/port_platform.h> - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/endpoint.h" - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include <uv.h> - -extern grpc_core::TraceFlag grpc_tcp_trace; - -#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 - -grpc_endpoint* grpc_tcp_create(uv_tcp_t* handle, - grpc_resource_quota* resource_quota, - char* peer_string); - -#endif /* GRPC_UV */ - -#endif /* GRPC_CORE_LIB_IOMGR_TCP_UV_H */ diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc index aab8edc888..04e6f11eee 100644 --- a/src/core/lib/iomgr/tcp_windows.cc +++ b/src/core/lib/iomgr/tcp_windows.cc @@ -51,7 +51,7 @@ #define GRPC_FIONBIO FIONBIO #endif -grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); +extern grpc_core::TraceFlag grpc_tcp_trace; static grpc_error* set_non_block(SOCKET sock) { int status; diff --git a/src/core/lib/iomgr/timer.cc b/src/core/lib/iomgr/timer.cc new file mode 100644 index 0000000000..e647cdefa0 --- /dev/null +++ b/src/core/lib/iomgr/timer.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +grpc_timer_vtable* grpc_timer_impl; + +void grpc_set_timer_impl(grpc_timer_vtable* vtable) { + grpc_timer_impl = vtable; +} + +void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + grpc_timer_impl->init(timer, deadline, closure); +} + +void grpc_timer_cancel(grpc_timer* timer) { grpc_timer_impl->cancel(timer); } + +grpc_timer_check_result grpc_timer_check(grpc_millis* next) { + return grpc_timer_impl->check(next); +} + +void grpc_timer_list_init() { grpc_timer_impl->list_init(); } + +void grpc_timer_list_shutdown() { grpc_timer_impl->list_shutdown(); } + +void grpc_timer_consume_kick() { grpc_timer_impl->consume_kick(); } diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h index 67f1b1b3f9..5ff10d3aee 100644 --- a/src/core/lib/iomgr/timer.h +++ b/src/core/lib/iomgr/timer.h @@ -23,17 +23,41 @@ #include "src/core/lib/iomgr/port.h" -#ifdef GRPC_UV -#include "src/core/lib/iomgr/timer_uv.h" -#else -#include "src/core/lib/iomgr/timer_generic.h" -#endif /* GRPC_UV */ - #include <grpc/support/time.h> #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr.h" -typedef struct grpc_timer grpc_timer; +typedef struct grpc_timer { + gpr_atm deadline; + uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ + bool pending; + struct grpc_timer* next; + struct grpc_timer* prev; + grpc_closure* closure; +#ifndef NDEBUG + struct grpc_timer* hash_table_next; +#endif + + // Optional field used by custom timers + void* custom_timer; +} grpc_timer; + +typedef enum { + GRPC_TIMERS_NOT_CHECKED, + GRPC_TIMERS_CHECKED_AND_EMPTY, + GRPC_TIMERS_FIRED, +} grpc_timer_check_result; + +typedef struct grpc_timer_vtable { + void (*init)(grpc_timer* timer, grpc_millis, grpc_closure* closure); + void (*cancel)(grpc_timer* timer); + + /* Internal API */ + grpc_timer_check_result (*check)(grpc_millis* next); + void (*list_init)(); + void (*list_shutdown)(void); + void (*consume_kick)(void); +} grpc_timer_vtable; /* Initialize *timer. When expired or canceled, closure will be called with error set to indicate if it expired (GRPC_ERROR_NONE) or was canceled @@ -78,12 +102,6 @@ void grpc_timer_cancel(grpc_timer* timer); /* iomgr internal api for dealing with timers */ -typedef enum { - GRPC_TIMERS_NOT_CHECKED, - GRPC_TIMERS_CHECKED_AND_EMPTY, - GRPC_TIMERS_FIRED, -} grpc_timer_check_result; - /* Check for timers to be run, and run them. Return true if timer callbacks were executed. If next is non-null, TRY to update *next with the next running timer @@ -99,7 +117,9 @@ void grpc_timer_list_shutdown(); void grpc_timer_consume_kick(void); /* the following must be implemented by each iomgr implementation */ - void grpc_kick_poller(void); +/* Sets the timer implementation */ +void grpc_set_timer_impl(grpc_timer_vtable* vtable); + #endif /* GRPC_CORE_LIB_IOMGR_TIMER_H */ diff --git a/src/core/lib/iomgr/timer_custom.cc b/src/core/lib/iomgr/timer_custom.cc new file mode 100644 index 0000000000..71d825ff9f --- /dev/null +++ b/src/core/lib/iomgr/timer_custom.cc @@ -0,0 +1,93 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_custom.h" + +static grpc_custom_timer_vtable* custom_timer_impl; + +void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_core::ExecCtx exec_ctx; + grpc_timer* timer = t->original; + GPR_ASSERT(timer->pending); + timer->pending = 0; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); + custom_timer_impl->stop(t); + gpr_free(t); +} + +static void timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + uint64_t timeout; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + if (deadline <= grpc_core::ExecCtx::Get()->Now()) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + timer->pending = false; + return; + } else { + timeout = deadline - now; + } + timer->pending = true; + timer->closure = closure; + grpc_custom_timer* timer_wrapper = + (grpc_custom_timer*)gpr_malloc(sizeof(grpc_custom_timer)); + timer_wrapper->timeout_ms = timeout; + timer->custom_timer = (void*)timer_wrapper; + timer_wrapper->original = timer; + custom_timer_impl->start(timer_wrapper); +} + +static void timer_cancel(grpc_timer* timer) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_custom_timer* tw = (grpc_custom_timer*)timer->custom_timer; + if (timer->pending) { + timer->pending = 0; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED); + custom_timer_impl->stop(tw); + gpr_free(tw); + } +} + +static grpc_timer_check_result timer_check(grpc_millis* next) { + return GRPC_TIMERS_NOT_CHECKED; +} + +static void timer_list_init() {} +static void timer_list_shutdown() {} + +static void timer_consume_kick(void) {} + +static grpc_timer_vtable custom_timer_vtable = { + timer_init, timer_cancel, timer_check, + timer_list_init, timer_list_shutdown, timer_consume_kick}; + +void grpc_custom_timer_init(grpc_custom_timer_vtable* impl) { + custom_timer_impl = impl; + grpc_set_timer_impl(&custom_timer_vtable); +} diff --git a/src/core/lib/iomgr/timer_custom.h b/src/core/lib/iomgr/timer_custom.h new file mode 100644 index 0000000000..bfea8bafa6 --- /dev/null +++ b/src/core/lib/iomgr/timer_custom.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/timer.h" + +typedef struct grpc_custom_timer { + // Implementation defined + void* timer; + uint64_t timeout_ms; + + grpc_timer* original; +} grpc_custom_timer; + +typedef struct grpc_custom_timer_vtable { + void (*start)(grpc_custom_timer* t); + void (*stop)(grpc_custom_timer* t); +} grpc_custom_timer_vtable; + +void grpc_custom_timer_init(grpc_custom_timer_vtable* impl); + +void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error); + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H */ diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc index 52a571f425..93e654b7fa 100644 --- a/src/core/lib/iomgr/timer_generic.cc +++ b/src/core/lib/iomgr/timer_generic.cc @@ -22,8 +22,6 @@ #include <inttypes.h> -#ifdef GRPC_TIMER_USE_GENERIC - #include "src/core/lib/iomgr/timer.h" #include <grpc/support/alloc.h> @@ -238,7 +236,7 @@ static gpr_atm compute_min_deadline(timer_shard* shard) { : grpc_timer_heap_top(&shard->heap)->deadline; } -void grpc_timer_list_init() { +static void timer_list_init() { uint32_t i; g_num_shards = GPR_MIN(1, 2 * gpr_cpu_num_cores()); @@ -270,7 +268,7 @@ void grpc_timer_list_init() { INIT_TIMER_HASH_TABLE(); } -void grpc_timer_list_shutdown() { +static void timer_list_shutdown() { size_t i; run_some_expired_timers( GPR_ATM_MAX, nullptr, @@ -326,8 +324,8 @@ static void note_deadline_change(timer_shard* shard) { void grpc_timer_init_unset(grpc_timer* timer) { timer->pending = false; } -void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, - grpc_closure* closure) { +static void timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { int is_first_timer = 0; timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)]; timer->closure = closure; @@ -412,12 +410,12 @@ void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, } } -void grpc_timer_consume_kick(void) { +static void timer_consume_kick(void) { /* force re-evaluation of last seeen min */ gpr_tls_set(&g_last_seen_min_timer, 0); } -void grpc_timer_cancel(grpc_timer* timer) { +static void timer_cancel(grpc_timer* timer) { if (!g_shared_mutables.initialized) { /* must have already been cancelled, also the shard mutex is invalid */ return; @@ -604,7 +602,7 @@ static grpc_timer_check_result run_some_expired_timers(gpr_atm now, return result; } -grpc_timer_check_result grpc_timer_check(grpc_millis* next) { +static grpc_timer_check_result timer_check(grpc_millis* next) { // prelude grpc_millis now = grpc_core::ExecCtx::Get()->Now(); @@ -660,4 +658,6 @@ grpc_timer_check_result grpc_timer_check(grpc_millis* next) { return r; } -#endif /* GRPC_TIMER_USE_GENERIC */ +grpc_timer_vtable grpc_generic_timer_vtable = { + timer_init, timer_cancel, timer_check, + timer_list_init, timer_list_shutdown, timer_consume_kick}; diff --git a/src/core/lib/iomgr/timer_heap.cc b/src/core/lib/iomgr/timer_heap.cc index e5b5abfc97..0c17d607eb 100644 --- a/src/core/lib/iomgr/timer_heap.cc +++ b/src/core/lib/iomgr/timer_heap.cc @@ -20,8 +20,6 @@ #include "src/core/lib/iomgr/port.h" -#ifdef GRPC_TIMER_USE_GENERIC - #include "src/core/lib/iomgr/timer_heap.h" #include <string.h> @@ -135,5 +133,3 @@ grpc_timer* grpc_timer_heap_top(grpc_timer_heap* heap) { void grpc_timer_heap_pop(grpc_timer_heap* heap) { grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); } - -#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc index 0210e70015..94f288af27 100644 --- a/src/core/lib/iomgr/timer_manager.cc +++ b/src/core/lib/iomgr/timer_manager.cc @@ -18,21 +18,20 @@ #include <grpc/support/port_platform.h> -#include "src/core/lib/iomgr/timer_manager.h" +#include <inttypes.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <inttypes.h> - #include "src/core/lib/debug/trace.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" -typedef struct completed_thread { - gpr_thd_id t; - struct completed_thread* next; -} completed_thread; +struct completed_thread { + grpc_core::Thread thd; + completed_thread* next; +}; extern grpc_core::TraceFlag grpc_timer_check_trace; @@ -68,7 +67,7 @@ static void gc_completed_threads(void) { g_completed_threads = nullptr; gpr_mu_unlock(&g_mu); while (to_gc != nullptr) { - gpr_thd_join(to_gc->t); + to_gc->thd.Join(); completed_thread* next = to_gc->next; gpr_free(to_gc); to_gc = next; @@ -85,18 +84,10 @@ static void start_timer_thread_and_unlock(void) { if (grpc_timer_check_trace.enabled()) { gpr_log(GPR_DEBUG, "Spawn timer thread"); } - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); completed_thread* ct = static_cast<completed_thread*>(gpr_malloc(sizeof(*ct))); - // The call to gpr_thd_new() has to be under the same lock used by - // gc_completed_threads(), particularly due to ct->t, which is written here - // (internally by gpr_thd_new) and read there. Otherwise it's possible for ct - // to leak through g_completed_threads and be freed in gc_completed_threads() - // before "&ct->t" is written to, causing a use-after-free. - gpr_mu_lock(&g_mu); - gpr_thd_new(&ct->t, "grpc_global_timer", timer_thread, ct, &opt); - gpr_mu_unlock(&g_mu); + ct->thd = grpc_core::Thread("grpc_global_timer", timer_thread, ct); + ct->thd.Start(); } void grpc_timer_manager_tick() { diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc index 6f28f553c5..dadeb960b2 100644 --- a/src/core/lib/iomgr/timer_uv.cc +++ b/src/core/lib/iomgr/timer_uv.cc @@ -20,20 +20,18 @@ #include "src/core/lib/iomgr/port.h" -#if GRPC_UV +#ifdef GRPC_UV #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/iomgr_custom.h" #include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_custom.h" #include <uv.h> -grpc_core::TraceFlag grpc_timer_trace(false, "timer"); -grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check"); - static void timer_close_callback(uv_handle_t* handle) { gpr_free(handle); } static void stop_uv_timer(uv_timer_t* handle) { @@ -43,57 +41,23 @@ static void stop_uv_timer(uv_timer_t* handle) { } void run_expired_timer(uv_timer_t* handle) { - grpc_timer* timer = (grpc_timer*)handle->data; - grpc_core::ExecCtx exec_ctx; - GRPC_UV_ASSERT_SAME_THREAD(); - GPR_ASSERT(timer->pending); - timer->pending = 0; - GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); - stop_uv_timer(handle); + grpc_custom_timer* timer_wrapper = (grpc_custom_timer*)handle->data; + grpc_custom_timer_callback(timer_wrapper, GRPC_ERROR_NONE); } -void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, - grpc_closure* closure) { - uint64_t timeout; +static void timer_start(grpc_custom_timer* t) { uv_timer_t* uv_timer; - GRPC_UV_ASSERT_SAME_THREAD(); - timer->closure = closure; - if (deadline <= grpc_core::ExecCtx::Get()->Now()) { - timer->pending = 0; - GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); - return; - } - timer->pending = 1; - timeout = (uint64_t)(deadline - grpc_core::ExecCtx::Get()->Now()); uv_timer = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); uv_timer_init(uv_default_loop(), uv_timer); - uv_timer->data = timer; - timer->uv_timer = uv_timer; - uv_timer_start(uv_timer, run_expired_timer, timeout, 0); - /* We assume that gRPC timers are only used alongside other active gRPC - objects, and that there will therefore always be something else keeping - the uv loop alive whenever there is a timer */ - uv_unref((uv_handle_t*)uv_timer); + uv_timer->data = t; + t->timer = (void*)uv_timer; + uv_timer_start(uv_timer, run_expired_timer, t->timeout_ms, 0); } -void grpc_timer_init_unset(grpc_timer* timer) { timer->pending = 0; } - -void grpc_timer_cancel(grpc_timer* timer) { - GRPC_UV_ASSERT_SAME_THREAD(); - if (timer->pending) { - timer->pending = 0; - GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED); - stop_uv_timer((uv_timer_t*)timer->uv_timer); - } +static void timer_stop(grpc_custom_timer* t) { + stop_uv_timer((uv_timer_t*)t->timer); } -grpc_timer_check_result grpc_timer_check(grpc_millis* next) { - return GRPC_TIMERS_NOT_CHECKED; -} - -void grpc_timer_list_init() {} -void grpc_timer_list_shutdown() {} - -void grpc_timer_consume_kick(void) {} +grpc_custom_timer_vtable uv_timer_vtable = {timer_start, timer_stop}; -#endif /* GRPC_UV */ +#endif diff --git a/src/core/lib/iomgr/udp_server.cc b/src/core/lib/iomgr/udp_server.cc index ec65497d79..15a242abe2 100644 --- a/src/core/lib/iomgr/udp_server.cc +++ b/src/core/lib/iomgr/udp_server.cc @@ -52,6 +52,8 @@ #include <grpc/support/time.h> #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/executor.h" @@ -62,41 +64,102 @@ #include "src/core/lib/iomgr/socket_utils_posix.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" -/* one listening port */ -typedef struct grpc_udp_listener grpc_udp_listener; -struct grpc_udp_listener { - int fd; - grpc_fd* emfd; - grpc_udp_server* server; - grpc_resolved_address addr; - grpc_closure read_closure; - grpc_closure write_closure; +/* A listener which implements basic features of Listening on a port for + * I/O events*/ +class GrpcUdpListener { + public: + GrpcUdpListener(grpc_udp_server* server, int fd, + const grpc_resolved_address* addr); + ~GrpcUdpListener(); + + /* Called when grpc server starts to listening on the grpc_fd. */ + void StartListening(grpc_pollset** pollsets, size_t pollset_count, + GrpcUdpHandlerFactory* handler_factory); + + /* Called when data is available to read from the socket. + * Return true if there is more data to read from fd. */ + void OnRead(grpc_error* error, void* do_read_arg); + + /* Called when the socket is writeable. The given closure should be scheduled + * when the socket becomes blocked next time. */ + void OnCanWrite(grpc_error* error, void* do_write_arg); + + /* Called when the grpc_fd is about to be orphaned (and the FD closed). */ + void OnFdAboutToOrphan(); + + /* Called to orphan fd of this listener.*/ + void OrphanFd(); + + /* Called when this listener is going to be destroyed. */ + void OnDestroy(); + + int fd() const { return fd_; } + + protected: + grpc_fd* emfd() const { return emfd_; } + + gpr_mu* mutex() { return &mutex_; } + + private: + /* event manager callback when reads are ready */ + static void on_read(void* arg, grpc_error* error); + static void on_write(void* arg, grpc_error* error); + + static void do_read(void* arg, grpc_error* error); + static void do_write(void* arg, grpc_error* error); + // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback + // interface. + static void fd_notify_on_write_wrapper(void* arg, grpc_error* error); + + static void shutdown_fd(void* args, grpc_error* error); + + int fd_; + grpc_fd* emfd_; + grpc_udp_server* server_; + grpc_resolved_address addr_; + grpc_closure read_closure_; + grpc_closure write_closure_; // To be called when corresponding QuicGrpcServer closes all active // connections. - grpc_closure orphan_fd_closure; - grpc_closure destroyed_closure; - grpc_udp_server_read_cb read_cb; - grpc_udp_server_write_cb write_cb; - grpc_udp_server_orphan_cb orphan_cb; - grpc_udp_server_start_cb start_cb; + grpc_closure orphan_fd_closure_; + grpc_closure destroyed_closure_; // To be scheduled on another thread to actually read/write. - grpc_closure do_read_closure; - grpc_closure do_write_closure; - grpc_closure notify_on_write_closure; + grpc_closure do_read_closure_; + grpc_closure do_write_closure_; + grpc_closure notify_on_write_closure_; // True if orphan_cb is trigered. - bool orphan_notified; + bool orphan_notified_; // True if grpc_fd_notify_on_write() is called after on_write() call. - bool notify_on_write_armed; + bool notify_on_write_armed_; // True if fd has been shutdown. - bool already_shutdown; - - struct grpc_udp_listener* next; + bool already_shutdown_; + // Object actually handles I/O events. Assigned in StartListening(). + GrpcUdpHandler* udp_handler_ = nullptr; + // To be notified on destruction. + GrpcUdpHandlerFactory* handler_factory_ = nullptr; + // Required to access above fields. + gpr_mu mutex_; }; -struct shutdown_fd_args { - grpc_udp_listener* sp; - gpr_mu* server_mu; -}; +GrpcUdpListener::GrpcUdpListener(grpc_udp_server* server, int fd, + const grpc_resolved_address* addr) + : fd_(fd), + server_(server), + orphan_notified_(false), + already_shutdown_(false) { + char* addr_str; + char* name; + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_asprintf(&name, "udp-server-listener:%s", addr_str); + gpr_free(addr_str); + emfd_ = grpc_fd_create(fd, name); + memcpy(&addr_, addr, sizeof(grpc_resolved_address)); + GPR_ASSERT(emfd_); + gpr_free(name); + gpr_mu_init(&mutex_); +} + +GrpcUdpListener::~GrpcUdpListener() { gpr_mu_destroy(&mutex_); } /* the overall server */ struct grpc_udp_server { @@ -113,10 +176,11 @@ struct grpc_udp_server { /* is this server shutting down? (boolean) */ int shutdown; - /* linked list of server ports */ - grpc_udp_listener* head; - grpc_udp_listener* tail; - unsigned nports; + /* An array of listeners */ + grpc_core::InlinedVector<GrpcUdpListener, 16> listeners; + + /* factory for use to create udp listeners */ + GrpcUdpHandlerFactory* handler_factory; /* shutdown callback */ grpc_closure* shutdown_complete; @@ -141,8 +205,7 @@ static grpc_socket_factory* get_socket_factory(const grpc_channel_args* args) { } grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) { - grpc_udp_server* s = - static_cast<grpc_udp_server*>(gpr_malloc(sizeof(grpc_udp_server))); + grpc_udp_server* s = grpc_core::New<grpc_udp_server>(); gpr_mu_init(&s->mu); s->socket_factory = get_socket_factory(args); if (s->socket_factory) { @@ -151,33 +214,27 @@ grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) { s->active_ports = 0; s->destroyed_ports = 0; s->shutdown = 0; - s->head = nullptr; - s->tail = nullptr; - s->nports = 0; - return s; } -static void shutdown_fd(void* args, grpc_error* error) { - struct shutdown_fd_args* shutdown_args = - static_cast<struct shutdown_fd_args*>(args); - grpc_udp_listener* sp = shutdown_args->sp; - gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd); - gpr_mu_lock(shutdown_args->server_mu); - grpc_fd_shutdown(sp->emfd, GRPC_ERROR_REF(error)); - sp->already_shutdown = true; - if (!sp->notify_on_write_armed) { +// static +void GrpcUdpListener::shutdown_fd(void* args, grpc_error* error) { + if (args == nullptr) { + // No-op if shutdown args are null. + return; + } + auto sp = static_cast<GrpcUdpListener*>(args); + gpr_mu_lock(sp->mutex()); + gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd_); + grpc_fd_shutdown(sp->emfd_, GRPC_ERROR_REF(error)); + sp->already_shutdown_ = true; + if (!sp->notify_on_write_armed_) { // Re-arm write notification to notify listener with error. This is // necessary to decrement active_ports. - sp->notify_on_write_armed = true; - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); + sp->notify_on_write_armed_ = true; + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); } - gpr_mu_unlock(shutdown_args->server_mu); - gpr_free(shutdown_args); -} - -static void dummy_cb(void* arg, grpc_error* error) { - // No-op. + gpr_mu_unlock(sp->mutex()); } static void finish_shutdown(grpc_udp_server* s) { @@ -188,24 +245,22 @@ static void finish_shutdown(grpc_udp_server* s) { gpr_mu_destroy(&s->mu); gpr_log(GPR_DEBUG, "Destroy all listeners."); - while (s->head) { - grpc_udp_listener* sp = s->head; - s->head = sp->next; - gpr_free(sp); + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].OnDestroy(); } if (s->socket_factory) { grpc_socket_factory_unref(s->socket_factory); } - gpr_free(s); + grpc_core::Delete(s); } static void destroyed_port(void* server, grpc_error* error) { grpc_udp_server* s = static_cast<grpc_udp_server*>(server); gpr_mu_lock(&s->mu); s->destroyed_ports++; - if (s->destroyed_ports == s->nports) { + if (s->destroyed_ports == s->listeners.size()) { gpr_mu_unlock(&s->mu); finish_shutdown(s); } else { @@ -222,35 +277,30 @@ static void deactivated_all_ports(grpc_udp_server* s) { GPR_ASSERT(s->shutdown); - if (s->head) { - grpc_udp_listener* sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_unlink_if_unix_domain_socket(&sp->addr); - - GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, - grpc_schedule_on_exec_ctx); - if (!sp->orphan_notified) { - /* Call the orphan_cb to signal that the FD is about to be closed and - * should no longer be used. Because at this point, all listening ports - * have been shutdown already, no need to shutdown again.*/ - GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, dummy_cb, sp, - grpc_schedule_on_exec_ctx); - GPR_ASSERT(sp->orphan_cb); - gpr_log(GPR_DEBUG, "Orphan fd %d", sp->fd); - sp->orphan_cb(sp->emfd, &sp->orphan_fd_closure, sp->server->user_data); - } - grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, nullptr, - false /* already_closed */, "udp_listener_shutdown"); - } - gpr_mu_unlock(&s->mu); - } else { + if (s->listeners.size() == 0) { gpr_mu_unlock(&s->mu); finish_shutdown(s); + return; } + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].OrphanFd(); + } + gpr_mu_unlock(&s->mu); +} + +void GrpcUdpListener::OrphanFd() { + gpr_log(GPR_DEBUG, "Orphan fd %d, emfd %p", fd_, emfd_); + grpc_unlink_if_unix_domain_socket(&addr_); + + GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_, + grpc_schedule_on_exec_ctx); + /* Because at this point, all listening sockets have been shutdown already, no + * need to call OnFdAboutToOrphan() to notify the handler again. */ + grpc_fd_orphan(emfd_, &destroyed_closure_, nullptr, + false /* already_closed */, "udp_listener_shutdown"); } void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { - grpc_udp_listener* sp; gpr_mu_lock(&s->mu); GPR_ASSERT(!s->shutdown); @@ -261,16 +311,9 @@ void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { gpr_log(GPR_DEBUG, "start to destroy udp_server"); /* shutdown all fd's */ if (s->active_ports) { - for (sp = s->head; sp; sp = sp->next) { - GPR_ASSERT(sp->orphan_cb); - struct shutdown_fd_args* args = - static_cast<struct shutdown_fd_args*>(gpr_malloc(sizeof(*args))); - args->sp = sp; - args->server_mu = &s->mu; - GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, shutdown_fd, args, - grpc_schedule_on_exec_ctx); - sp->orphan_cb(sp->emfd, &sp->orphan_fd_closure, sp->server->user_data); - sp->orphan_notified = true; + for (size_t i = 0; i < s->listeners.size(); ++i) { + GrpcUdpListener* sp = &s->listeners[i]; + sp->OnFdAboutToOrphan(); } gpr_mu_unlock(&s->mu); } else { @@ -279,12 +322,30 @@ void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { } } +void GrpcUdpListener::OnFdAboutToOrphan() { + gpr_mu_lock(&mutex_); + grpc_unlink_if_unix_domain_socket(&addr_); + + GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_, + grpc_schedule_on_exec_ctx); + if (!orphan_notified_ && udp_handler_ != nullptr) { + /* Singals udp_handler that the FD is about to be closed and + * should no longer be used. */ + GRPC_CLOSURE_INIT(&orphan_fd_closure_, shutdown_fd, this, + grpc_schedule_on_exec_ctx); + gpr_log(GPR_DEBUG, "fd %d about to be orphaned", fd_); + udp_handler_->OnFdAboutToOrphan(&orphan_fd_closure_, server_->user_data); + orphan_notified_ = true; + } + gpr_mu_unlock(&mutex_); +} + static int bind_socket(grpc_socket_factory* socket_factory, int sockfd, const grpc_resolved_address* addr) { return (socket_factory != nullptr) ? grpc_socket_factory_bind(socket_factory, sockfd, addr) : bind(sockfd, - reinterpret_cast<struct sockaddr*>( + reinterpret_cast<grpc_sockaddr*>( const_cast<char*>(addr->addr)), static_cast<socklen_t>(addr->len)); } @@ -294,8 +355,8 @@ static int prepare_socket(grpc_socket_factory* socket_factory, int fd, const grpc_resolved_address* addr, int rcv_buf_size, int snd_buf_size) { grpc_resolved_address sockname_temp; - struct sockaddr* addr_ptr = - reinterpret_cast<struct sockaddr*>(const_cast<char*>(addr->addr)); + grpc_sockaddr* addr_ptr = + reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)); if (fd < 0) { goto error; @@ -331,7 +392,7 @@ static int prepare_socket(grpc_socket_factory* socket_factory, int fd, sockname_temp.len = sizeof(struct sockaddr_storage); - if (getsockname(fd, reinterpret_cast<struct sockaddr*>(sockname_temp.addr), + if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr), reinterpret_cast<socklen_t*>(&sockname_temp.len)) < 0) { goto error; } @@ -364,163 +425,140 @@ error: return -1; } -static void do_read(void* arg, grpc_error* error) { - grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg); - GPR_ASSERT(sp->read_cb && error == GRPC_ERROR_NONE); +// static +void GrpcUdpListener::do_read(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + GPR_ASSERT(error == GRPC_ERROR_NONE); /* TODO: the reason we hold server->mu here is merely to prevent fd * shutdown while we are reading. However, it blocks do_write(). Switch to * read lock if available. */ - gpr_mu_lock(&sp->server->mu); + gpr_mu_lock(sp->mutex()); /* Tell the registered callback that data is available to read. */ - if (!sp->already_shutdown && sp->read_cb(sp->emfd)) { + if (!sp->already_shutdown_ && sp->udp_handler_->Read()) { /* There maybe more packets to read. Schedule read_more_cb_ closure to run * after finishing this event loop. */ - GRPC_CLOSURE_SCHED(&sp->do_read_closure, GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(&sp->do_read_closure_, GRPC_ERROR_NONE); } else { /* Finish reading all the packets, re-arm the notification event so we can * get another chance to read. Or fd already shutdown, re-arm to get a * notification with shutdown error. */ - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + grpc_fd_notify_on_read(sp->emfd_, &sp->read_closure_); } - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(sp->mutex()); } -/* event manager callback when reads are ready */ -static void on_read(void* arg, grpc_error* error) { - grpc_udp_listener* sp = static_cast<grpc_udp_listener*>(arg); +// static +void GrpcUdpListener::on_read(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + sp->OnRead(error, arg); +} - gpr_mu_lock(&sp->server->mu); +void GrpcUdpListener::OnRead(grpc_error* error, void* do_read_arg) { if (error != GRPC_ERROR_NONE) { - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(sp->server); + gpr_mu_lock(&server_->mu); + if (0 == --server_->active_ports && server_->shutdown) { + gpr_mu_unlock(&server_->mu); + deactivated_all_ports(server_); } else { - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(&server_->mu); } return; } + /* Read once. If there is more data to read, off load the work to another * thread to finish. */ - GPR_ASSERT(sp->read_cb); - if (sp->read_cb(sp->emfd)) { + if (udp_handler_->Read()) { /* There maybe more packets to read. Schedule read_more_cb_ closure to run * after finishing this event loop. */ - GRPC_CLOSURE_INIT(&sp->do_read_closure, do_read, arg, + GRPC_CLOSURE_INIT(&do_read_closure_, do_read, do_read_arg, grpc_executor_scheduler(GRPC_EXECUTOR_LONG)); - GRPC_CLOSURE_SCHED(&sp->do_read_closure, GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(&do_read_closure_, GRPC_ERROR_NONE); } else { /* Finish reading all the packets, re-arm the notification event so we can * get another chance to read. Or fd already shutdown, re-arm to get a * notification with shutdown error. */ - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + grpc_fd_notify_on_read(emfd_, &read_closure_); } - gpr_mu_unlock(&sp->server->mu); } +// static // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback interface. -void fd_notify_on_write_wrapper(void* arg, grpc_error* error) { - grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg); - gpr_mu_lock(&sp->server->mu); - if (!sp->notify_on_write_armed) { - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); - sp->notify_on_write_armed = true; - } - gpr_mu_unlock(&sp->server->mu); +void GrpcUdpListener::fd_notify_on_write_wrapper(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + gpr_mu_lock(sp->mutex()); + if (!sp->notify_on_write_armed_) { + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); + sp->notify_on_write_armed_ = true; + } + gpr_mu_unlock(sp->mutex()); } -static void do_write(void* arg, grpc_error* error) { - grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg); - gpr_mu_lock(&sp->server->mu); - if (sp->already_shutdown) { +// static +void GrpcUdpListener::do_write(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + gpr_mu_lock(sp->mutex()); + if (sp->already_shutdown_) { // If fd has been shutdown, don't write any more and re-arm notification. - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); } else { - sp->notify_on_write_armed = false; + sp->notify_on_write_armed_ = false; /* Tell the registered callback that the socket is writeable. */ - GPR_ASSERT(sp->write_cb && error == GRPC_ERROR_NONE); - GRPC_CLOSURE_INIT(&sp->notify_on_write_closure, fd_notify_on_write_wrapper, + GPR_ASSERT(error == GRPC_ERROR_NONE); + GRPC_CLOSURE_INIT(&sp->notify_on_write_closure_, fd_notify_on_write_wrapper, arg, grpc_schedule_on_exec_ctx); - sp->write_cb(sp->emfd, sp->server->user_data, &sp->notify_on_write_closure); + sp->udp_handler_->OnCanWrite(sp->server_->user_data, + &sp->notify_on_write_closure_); } - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(sp->mutex()); } -static void on_write(void* arg, grpc_error* error) { - grpc_udp_listener* sp = static_cast<grpc_udp_listener*>(arg); +// static +void GrpcUdpListener::on_write(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + sp->OnCanWrite(error, arg); +} - gpr_mu_lock(&sp->server->mu); +void GrpcUdpListener::OnCanWrite(grpc_error* error, void* do_write_arg) { if (error != GRPC_ERROR_NONE) { - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(sp->server); + gpr_mu_lock(&server_->mu); + if (0 == --server_->active_ports && server_->shutdown) { + gpr_mu_unlock(&server_->mu); + deactivated_all_ports(server_); } else { - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(&server_->mu); } return; } /* Schedule actual write in another thread. */ - GRPC_CLOSURE_INIT(&sp->do_write_closure, do_write, arg, + GRPC_CLOSURE_INIT(&do_write_closure_, do_write, do_write_arg, grpc_executor_scheduler(GRPC_EXECUTOR_LONG)); - GRPC_CLOSURE_SCHED(&sp->do_write_closure, GRPC_ERROR_NONE); - gpr_mu_unlock(&sp->server->mu); + GRPC_CLOSURE_SCHED(&do_write_closure_, GRPC_ERROR_NONE); } static int add_socket_to_server(grpc_udp_server* s, int fd, const grpc_resolved_address* addr, - int rcv_buf_size, int snd_buf_size, - grpc_udp_server_start_cb start_cb, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb) { - grpc_udp_listener* sp; - int port; - char* addr_str; - char* name; + int rcv_buf_size, int snd_buf_size) { + gpr_log(GPR_DEBUG, "add socket %d to server", fd); - port = + int port = prepare_socket(s->socket_factory, fd, addr, rcv_buf_size, snd_buf_size); if (port >= 0) { - grpc_sockaddr_to_string(&addr_str, addr, 1); - gpr_asprintf(&name, "udp-server-listener:%s", addr_str); - gpr_free(addr_str); gpr_mu_lock(&s->mu); - s->nports++; - sp = static_cast<grpc_udp_listener*>(gpr_malloc(sizeof(grpc_udp_listener))); - sp->next = nullptr; - if (s->head == nullptr) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->fd = fd; - sp->emfd = grpc_fd_create(fd, name); - memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); - sp->read_cb = read_cb; - sp->write_cb = write_cb; - sp->orphan_cb = orphan_cb; - sp->start_cb = start_cb; - sp->orphan_notified = false; - sp->already_shutdown = false; - GPR_ASSERT(sp->emfd); + s->listeners.emplace_back(s, fd, addr); + gpr_log(GPR_DEBUG, + "add socket %d to server for port %d, %zu listener(s) in total", fd, + port, s->listeners.size()); gpr_mu_unlock(&s->mu); - gpr_free(name); } - return port; } int grpc_udp_server_add_port(grpc_udp_server* s, const grpc_resolved_address* addr, int rcv_buf_size, int snd_buf_size, - grpc_udp_server_start_cb start_cb, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb) { - grpc_udp_listener* sp; + GrpcUdpHandlerFactory* handler_factory) { int allocated_port1 = -1; int allocated_port2 = -1; int fd; @@ -536,12 +574,11 @@ int grpc_udp_server_add_port(grpc_udp_server* s, /* Check if this is a wildcard port, and if so, try to keep the port the same as some previously created listener. */ if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { + for (size_t i = 0; i < s->listeners.size(); ++i) { sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == - getsockname(sp->fd, - reinterpret_cast<struct sockaddr*>(sockname_temp.addr), - reinterpret_cast<socklen_t*>(&sockname_temp.len))) { + if (0 == getsockname(s->listeners[i].fd(), + reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr), + reinterpret_cast<socklen_t*>(&sockname_temp.len))) { port = grpc_sockaddr_get_port(&sockname_temp); if (port > 0) { allocated_addr = static_cast<grpc_resolved_address*>( @@ -559,6 +596,7 @@ int grpc_udp_server_add_port(grpc_udp_server* s, addr = &addr6_v4mapped; } + s->handler_factory = handler_factory; /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ if (grpc_sockaddr_is_wildcard(addr, &port)) { grpc_sockaddr_make_wildcards(port, &wild4, &wild6); @@ -569,8 +607,7 @@ int grpc_udp_server_add_port(grpc_udp_server* s, GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); allocated_port1 = - add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size, start_cb, - read_cb, write_cb, orphan_cb); + add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size); if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { goto done; } @@ -593,8 +630,7 @@ int grpc_udp_server_add_port(grpc_udp_server* s, addr = &addr4_copy; } allocated_port2 = - add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size, start_cb, - read_cb, write_cb, orphan_cb); + add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size); done: gpr_free(allocated_addr); @@ -602,52 +638,55 @@ done: } int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index) { - grpc_udp_listener* sp; - if (port_index >= s->nports) { + if (port_index >= s->listeners.size()) { return -1; } - for (sp = s->head; sp && port_index != 0; sp = sp->next) { - --port_index; - } - GPR_ASSERT(sp); // if this fails, our check earlier was bogus - return sp->fd; + return s->listeners[port_index].fd(); } void grpc_udp_server_start(grpc_udp_server* s, grpc_pollset** pollsets, size_t pollset_count, void* user_data) { gpr_log(GPR_DEBUG, "grpc_udp_server_start"); - size_t i; gpr_mu_lock(&s->mu); - grpc_udp_listener* sp; GPR_ASSERT(s->active_ports == 0); s->pollsets = pollsets; s->user_data = user_data; - sp = s->head; - while (sp != nullptr) { - sp->start_cb(sp->emfd, sp->server->user_data); - for (i = 0; i < pollset_count; i++) { - grpc_pollset_add_fd(pollsets[i], sp->emfd); - } - GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].StartListening(pollsets, pollset_count, s->handler_factory); + } - GRPC_CLOSURE_INIT(&sp->write_closure, on_write, sp, - grpc_schedule_on_exec_ctx); - sp->notify_on_write_armed = true; - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); + gpr_mu_unlock(&s->mu); +} - /* Registered for both read and write callbacks: increment active_ports - * twice to account for this, and delay free-ing of memory until both - * on_read and on_write have fired. */ - s->active_ports += 2; +void GrpcUdpListener::StartListening(grpc_pollset** pollsets, + size_t pollset_count, + GrpcUdpHandlerFactory* handler_factory) { + gpr_mu_lock(&mutex_); + handler_factory_ = handler_factory; + udp_handler_ = handler_factory->CreateUdpHandler(emfd_, server_->user_data); + for (size_t i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(pollsets[i], emfd_); + } + GRPC_CLOSURE_INIT(&read_closure_, on_read, this, grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(emfd_, &read_closure_); + + GRPC_CLOSURE_INIT(&write_closure_, on_write, this, grpc_schedule_on_exec_ctx); + notify_on_write_armed_ = true; + grpc_fd_notify_on_write(emfd_, &write_closure_); + + /* Registered for both read and write callbacks: increment active_ports + * twice to account for this, and delay free-ing of memory until both + * on_read and on_write have fired. */ + server_->active_ports += 2; + gpr_mu_unlock(&mutex_); +} - sp = sp->next; +void GrpcUdpListener::OnDestroy() { + if (udp_handler_ != nullptr) { + handler_factory_->DestroyUdpHandler(udp_handler_); } - - gpr_mu_unlock(&s->mu); } #endif diff --git a/src/core/lib/iomgr/udp_server.h b/src/core/lib/iomgr/udp_server.h index 1be4d04dbb..4e384d2cdf 100644 --- a/src/core/lib/iomgr/udp_server.h +++ b/src/core/lib/iomgr/udp_server.h @@ -21,6 +21,7 @@ #include <grpc/support/port_platform.h> +#include "src/core/lib/gprpp/abstract.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/resolve_address.h" @@ -32,22 +33,46 @@ struct grpc_server; /* Forward decl of grpc_udp_server */ typedef struct grpc_udp_server grpc_udp_server; -/* Called when grpc server starts to listening on the grpc_fd. */ -typedef void (*grpc_udp_server_start_cb)(grpc_fd* emfd, void* user_data); - -/* Called when data is available to read from the socket. - * Return true if there is more data to read from fd. */ -typedef bool (*grpc_udp_server_read_cb)(grpc_fd* emfd); - -/* Called when the socket is writeable. The given closure should be scheduled - * when the socket becomes blocked next time. */ -typedef void (*grpc_udp_server_write_cb)(grpc_fd* emfd, void* user_data, - grpc_closure* notify_on_write_closure); - -/* Called when the grpc_fd is about to be orphaned (and the FD closed). */ -typedef void (*grpc_udp_server_orphan_cb)(grpc_fd* emfd, - grpc_closure* shutdown_fd_callback, - void* user_data); +/* An interface associated with a socket. udp server delivers I/O event on that + * socket to the subclass of this interface which is created through + * GrpcUdpHandlerFactory. + * Its implementation should do the real IO work, e.g. read packet and write. */ +class GrpcUdpHandler { + public: + GrpcUdpHandler(grpc_fd* emfd, void* user_data) {} + virtual ~GrpcUdpHandler() {} + + // Interfaces to be implemented by subclasses to do the actual setup/tear down + // or I/O. + + // Called when data is available to read from the socket. Returns true if + // there is more data to read after this call. + virtual bool Read() GRPC_ABSTRACT; + // Called when socket becomes write unblocked. The given closure should be + // scheduled when the socket becomes blocked next time. + virtual void OnCanWrite(void* user_data, + grpc_closure* notify_on_write_closure) GRPC_ABSTRACT; + // Called before the gRPC FD is orphaned. Notify udp server to continue + // orphaning fd by scheduling the given closure, afterwards the associated fd + // will be closed. + virtual void OnFdAboutToOrphan(grpc_closure* orphan_fd_closure, + void* user_data) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +class GrpcUdpHandlerFactory { + public: + virtual ~GrpcUdpHandlerFactory() {} + /* Called when start to listen on a socket. + * Return an instance of the implementation of GrpcUdpHandler interface which + * will process I/O events for this socket from now on. */ + virtual GrpcUdpHandler* CreateUdpHandler(grpc_fd* emfd, + void* user_data) GRPC_ABSTRACT; + virtual void DestroyUdpHandler(GrpcUdpHandler* handler) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; /* Create a server, initially not bound to any ports */ grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args); @@ -71,10 +96,7 @@ int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index); int grpc_udp_server_add_port(grpc_udp_server* s, const grpc_resolved_address* addr, int rcv_buf_size, int snd_buf_size, - grpc_udp_server_start_cb start_cb, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb); + GrpcUdpHandlerFactory* handler_factory); void grpc_udp_server_destroy(grpc_udp_server* server, grpc_closure* on_done); diff --git a/src/core/lib/iomgr/unix_sockets_posix.cc b/src/core/lib/iomgr/unix_sockets_posix.cc index 8d252fd331..5d09b4a9b1 100644 --- a/src/core/lib/iomgr/unix_sockets_posix.cc +++ b/src/core/lib/iomgr/unix_sockets_posix.cc @@ -66,15 +66,15 @@ grpc_error* grpc_resolve_unix_domain_address(const char* name, } int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); return addr->sa_family == AF_UNIX; } void grpc_unlink_if_unix_domain_socket( const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); if (addr->sa_family != AF_UNIX) { return; } @@ -89,8 +89,8 @@ void grpc_unlink_if_unix_domain_socket( char* grpc_sockaddr_to_uri_unix_if_possible( const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); if (addr->sa_family != AF_UNIX) { return nullptr; } diff --git a/src/core/lib/iomgr/wakeup_fd_cv.cc b/src/core/lib/iomgr/wakeup_fd_cv.cc index ee322105ae..74faa6379e 100644 --- a/src/core/lib/iomgr/wakeup_fd_cv.cc +++ b/src/core/lib/iomgr/wakeup_fd_cv.cc @@ -32,8 +32,8 @@ #include <grpc/support/sync.h> #include <grpc/support/time.h> -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #define MAX_TABLE_RESIZE 256 diff --git a/src/core/lib/profiling/basic_timers.cc b/src/core/lib/profiling/basic_timers.cc index ca6705a6b3..43384fd0ca 100644 --- a/src/core/lib/profiling/basic_timers.cc +++ b/src/core/lib/profiling/basic_timers.cc @@ -30,7 +30,7 @@ #include <string.h> #include "src/core/lib/gpr/env.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type; @@ -68,7 +68,7 @@ static pthread_cond_t g_cv; static gpr_timer_log_list g_in_progress_logs; static gpr_timer_log_list g_done_logs; static int g_shutdown; -static gpr_thd_id g_writing_thread; +static grpc_core::Thread* g_writing_thread; static __thread int g_thread_id; static int g_next_thread_id; static int g_writing_enabled = 1; @@ -182,7 +182,8 @@ static void finish_writing(void) { g_shutdown = 1; pthread_cond_signal(&g_cv); pthread_mutex_unlock(&g_mu); - gpr_thd_join(g_writing_thread); + g_writing_thread->Join(); + grpc_core::Delete(g_writing_thread); gpr_log(GPR_INFO, "flushing logs"); @@ -201,10 +202,8 @@ void gpr_timers_set_log_filename(const char* filename) { } static void init_output() { - gpr_thd_options options = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&options); - GPR_ASSERT(gpr_thd_new(&g_writing_thread, "timer_output_thread", - writing_thread, NULL, &options)); + g_writing_thread = grpc_core::New<grpc_core::Thread>("timer_output_thread", + writing_thread, nullptr); atexit(finish_writing); } diff --git a/src/core/lib/security/credentials/alts/alts_credentials.cc b/src/core/lib/security/credentials/alts/alts_credentials.cc new file mode 100644 index 0000000000..fa05d901bf --- /dev/null +++ b/src/core/lib/security/credentials/alts/alts_credentials.cc @@ -0,0 +1,119 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/credentials/alts/alts_credentials.h" + +#include <cstring> + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" +#include "src/core/lib/security/security_connector/alts_security_connector.h" + +#define GRPC_CREDENTIALS_TYPE_ALTS "Alts" +#define GRPC_ALTS_HANDSHAKER_SERVICE_URL "metadata.google.internal:8080" + +static void alts_credentials_destruct(grpc_channel_credentials* creds) { + grpc_alts_credentials* alts_creds = + reinterpret_cast<grpc_alts_credentials*>(creds); + grpc_alts_credentials_options_destroy(alts_creds->options); + gpr_free(alts_creds->handshaker_service_url); +} + +static void alts_server_credentials_destruct(grpc_server_credentials* creds) { + grpc_alts_server_credentials* alts_creds = + reinterpret_cast<grpc_alts_server_credentials*>(creds); + grpc_alts_credentials_options_destroy(alts_creds->options); + gpr_free(alts_creds->handshaker_service_url); +} + +static grpc_security_status alts_create_security_connector( + grpc_channel_credentials* creds, + grpc_call_credentials* request_metadata_creds, const char* target_name, + const grpc_channel_args* args, grpc_channel_security_connector** sc, + grpc_channel_args** new_args) { + return grpc_alts_channel_security_connector_create( + creds, request_metadata_creds, target_name, sc); +} + +static grpc_security_status alts_server_create_security_connector( + grpc_server_credentials* creds, grpc_server_security_connector** sc) { + return grpc_alts_server_security_connector_create(creds, sc); +} + +static const grpc_channel_credentials_vtable alts_credentials_vtable = { + alts_credentials_destruct, alts_create_security_connector, + /*duplicate_without_call_credentials=*/nullptr}; + +static const grpc_server_credentials_vtable alts_server_credentials_vtable = { + alts_server_credentials_destruct, alts_server_create_security_connector}; + +grpc_channel_credentials* grpc_alts_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts) { + if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) { + return nullptr; + } + auto creds = static_cast<grpc_alts_credentials*>( + gpr_zalloc(sizeof(grpc_alts_credentials))); + creds->options = grpc_alts_credentials_options_copy(options); + creds->handshaker_service_url = + handshaker_service_url == nullptr + ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL) + : gpr_strdup(handshaker_service_url); + creds->base.type = GRPC_CREDENTIALS_TYPE_ALTS; + creds->base.vtable = &alts_credentials_vtable; + gpr_ref_init(&creds->base.refcount, 1); + return &creds->base; +} + +grpc_server_credentials* grpc_alts_server_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts) { + if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) { + return nullptr; + } + auto creds = static_cast<grpc_alts_server_credentials*>( + gpr_zalloc(sizeof(grpc_alts_server_credentials))); + creds->options = grpc_alts_credentials_options_copy(options); + creds->handshaker_service_url = + handshaker_service_url == nullptr + ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL) + : gpr_strdup(handshaker_service_url); + creds->base.type = GRPC_CREDENTIALS_TYPE_ALTS; + creds->base.vtable = &alts_server_credentials_vtable; + gpr_ref_init(&creds->base.refcount, 1); + return &creds->base; +} + +grpc_channel_credentials* grpc_alts_credentials_create( + const grpc_alts_credentials_options* options) { + return grpc_alts_credentials_create_customized( + options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false); +} + +grpc_server_credentials* grpc_alts_server_credentials_create( + const grpc_alts_credentials_options* options) { + return grpc_alts_server_credentials_create_customized( + options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false); +} diff --git a/src/core/lib/security/credentials/alts/alts_credentials.h b/src/core/lib/security/credentials/alts/alts_credentials.h new file mode 100644 index 0000000000..621789cf65 --- /dev/null +++ b/src/core/lib/security/credentials/alts/alts_credentials.h @@ -0,0 +1,102 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H + +#include <grpc/support/port_platform.h> + +#include <grpc/grpc_security.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/lib/security/credentials/credentials.h" + +/* Main struct for grpc ALTS channel credential. */ +typedef struct grpc_alts_credentials { + grpc_channel_credentials base; + grpc_alts_credentials_options* options; + char* handshaker_service_url; +} grpc_alts_credentials; + +/* Main struct for grpc ALTS server credential. */ +typedef struct grpc_alts_server_credentials { + grpc_server_credentials base; + grpc_alts_credentials_options* options; + char* handshaker_service_url; +} grpc_alts_server_credentials; + +/** + * This method creates an ALTS channel credential object. + * + * - options: grpc ALTS credentials options instance for client. + * + * It returns the created ALTS channel credential object. + */ +grpc_channel_credentials* grpc_alts_credentials_create( + const grpc_alts_credentials_options* options); + +/** + * This method creates an ALTS server credential object. + * + * - options: grpc ALTS credentials options instance for server. + * + * It returns the created ALTS server credential object. + */ +grpc_server_credentials* grpc_alts_server_credentials_create( + const grpc_alts_credentials_options* options); + +/** + * This method creates an ALTS channel credential object with customized + * information provided by caller. + * + * - options: grpc ALTS credentials options instance for client. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". If it's nullptr, the address of default metadata server will + * be used. + * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted + * mode. This mode can be enabled when we are sure ALTS is running on GCP or + * for testing purpose. + * + * It returns nullptr if the flag is disabled AND ALTS is not running on GCP. + * Otherwise, it returns the created credential object. + */ + +grpc_channel_credentials* grpc_alts_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts); + +/** + * This method creates an ALTS server credential object with customized + * information provided by caller. + * + * - options: grpc ALTS credentials options instance for server. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". If it's nullptr, the address of default metadata server will + * be used. + * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted + * mode. This mode can be enabled when we are sure ALTS is running on GCP or + * for testing purpose. + * + * It returns nullptr if the flag is disabled and ALTS is not running on GCP. + * Otherwise, it returns the created credential object. + */ +grpc_server_credentials* grpc_alts_server_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H */ diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment.cc b/src/core/lib/security/credentials/alts/check_gcp_environment.cc new file mode 100644 index 0000000000..96807876cf --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +const size_t kBiosDataBufferSize = 256; + +static char* trim(const char* src) { + if (src == nullptr) { + return nullptr; + } + char* des = nullptr; + size_t start = 0, end = strlen(src) - 1; + /* find the last character that is not a whitespace. */ + while (end != 0 && isspace(src[end])) { + end--; + } + /* find the first character that is not a whitespace. */ + while (start < strlen(src) && isspace(src[start])) { + start++; + } + if (start <= end) { + des = static_cast<char*>( + gpr_zalloc(sizeof(char) * (end - start + 2 /* '\0' */))); + memcpy(des, src + start, end - start + 1); + } + return des; +} + +namespace grpc_core { +namespace internal { + +char* read_bios_file(const char* bios_file) { + FILE* fp = fopen(bios_file, "r"); + if (!fp) { + gpr_log(GPR_ERROR, "BIOS data file cannot be opened."); + return nullptr; + } + char buf[kBiosDataBufferSize + 1]; + size_t ret = fread(buf, sizeof(char), kBiosDataBufferSize, fp); + buf[ret] = '\0'; + char* trimmed_buf = trim(buf); + fclose(fp); + return trimmed_buf; +} + +} // namespace internal +} // namespace grpc_core diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment.h b/src/core/lib/security/credentials/alts/check_gcp_environment.h new file mode 100644 index 0000000000..aea4cea643 --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H + +namespace grpc_core { +namespace internal { + +/** + * This method is a helper function that reads a file containing system bios + * data. Exposed for testing only. + * + * - bios_file: a file containing BIOS data used to determine GCE tenancy + * information. + * + * It returns a buffer containing the data read from the file. + */ +char* read_bios_file(const char* bios_file); + +/** + * This method checks if system BIOS data contains Google-specific phrases. + * Exposed for testing only. + * + * - bios_data: a buffer containing system BIOS data. + * + * It returns true if the BIOS data contains Google-specific phrases, and false + * otherwise. + */ +bool check_bios_data(const char* bios_data); + +} // namespace internal +} // namespace grpc_core + +/** + * This method checks if a VM (Windows or Linux) is running within Google + * compute Engine (GCE) or not. It returns true if the VM is running in GCE and + * false otherwise. + */ +bool grpc_alts_is_running_on_gcp(); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H */ diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc new file mode 100644 index 0000000000..7c4d7a71cd --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_LINUX + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/sync.h> + +#include <string.h> + +#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google" +#define GRPC_ALTS_EXPECT_NAME_GCE "Google Compute Engine" +#define GRPC_ALTS_PRODUCT_NAME_FILE "/sys/class/dmi/id/product_name" + +static bool g_compute_engine_detection_done = false; +static bool g_is_on_compute_engine = false; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +namespace grpc_core { +namespace internal { + +bool check_bios_data(const char* bios_data_file) { + char* bios_data = read_bios_file(bios_data_file); + bool result = (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE)) || + (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GCE)); + gpr_free(bios_data); + return result; +} + +} // namespace internal +} // namespace grpc_core + +static void init_mu(void) { gpr_mu_init(&g_mu); } + +bool grpc_alts_is_running_on_gcp() { + gpr_once_init(&g_once, init_mu); + gpr_mu_lock(&g_mu); + if (!g_compute_engine_detection_done) { + g_is_on_compute_engine = + grpc_core::internal::check_bios_data(GRPC_ALTS_PRODUCT_NAME_FILE); + g_compute_engine_detection_done = true; + } + gpr_mu_unlock(&g_mu); + return g_is_on_compute_engine; +} + +#endif // GPR_LINUX diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc new file mode 100644 index 0000000000..d97681b86d --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS) + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <grpc/support/log.h> + +bool grpc_alts_is_running_on_gcp() { + gpr_log(GPR_ERROR, + "Platforms other than Linux and Windows are not supported"); + return false; +} + +#endif // !defined(LINUX) && !defined(GPR_WINDOWS) diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc new file mode 100644 index 0000000000..55efe0e9dd --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_WINDOWS + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <shellapi.h> +#include <stdio.h> +#include <tchar.h> +#include <windows.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> + +#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google" +#define GRPC_ALTS_WINDOWS_CHECK_COMMAND "powershell.exe" +#define GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS \ + "(Get-WmiObject -Class Win32_BIOS).Manufacturer" +#define GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE "windows_bios.data" + +const size_t kBiosDataBufferSize = 256; + +static bool g_compute_engine_detection_done = false; +static bool g_is_on_compute_engine = false; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +namespace grpc_core { +namespace internal { + +bool check_bios_data(const char* bios_data_file) { + char* bios_data = read_bios_file(bios_data_file); + bool result = !strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE); + remove(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE); + gpr_free(bios_data); + return result; +} + +} // namespace internal +} // namespace grpc_core + +static void init_mu(void) { gpr_mu_init(&g_mu); } + +static bool run_powershell() { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + HANDLE h = CreateFile(_T(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE), GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + gpr_log(GPR_ERROR, "CreateFile failed (%d).", GetLastError()); + return false; + } + PROCESS_INFORMATION pi; + STARTUPINFO si; + DWORD flags = CREATE_NO_WINDOW; + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = NULL; + si.hStdError = h; + si.hStdOutput = h; + TCHAR cmd[kBiosDataBufferSize]; + _sntprintf(cmd, kBiosDataBufferSize, _T("%s %s"), + _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND), + _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS)); + if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, + &pi)) { + gpr_log(GPR_ERROR, "CreateProcess failed (%d).\n", GetLastError()); + return false; + } + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(h); + return true; +} + +bool grpc_alts_is_running_on_gcp() { + gpr_once_init(&g_once, init_mu); + gpr_mu_lock(&g_mu); + if (!g_compute_engine_detection_done) { + g_is_on_compute_engine = + run_powershell() && + grpc_core::internal::check_bios_data(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE); + g_compute_engine_detection_done = true; + } + gpr_mu_unlock(&g_mu); + return g_is_on_compute_engine; +} + +#endif // GPR_WINDOWS diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc new file mode 100644 index 0000000000..7d54e8346f --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc @@ -0,0 +1,126 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stdlib.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +static grpc_alts_credentials_options* alts_client_options_copy( + const grpc_alts_credentials_options* options); + +static void alts_client_options_destroy(grpc_alts_credentials_options* options); + +static target_service_account* target_service_account_create( + const char* service_account) { + if (service_account == nullptr) { + return nullptr; + } + auto* sa = static_cast<target_service_account*>( + gpr_zalloc(sizeof(target_service_account))); + sa->data = gpr_strdup(service_account); + return sa; +} + +bool grpc_alts_credentials_client_options_add_target_service_account( + grpc_alts_credentials_client_options* options, + const char* service_account) { + if (options == nullptr || service_account == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_alts_credentials_client_options_add_target_service_account()"); + return false; + } + target_service_account* node = target_service_account_create(service_account); + node->next = options->target_account_list_head; + options->target_account_list_head = node; + return true; +} + +static void target_service_account_destroy( + target_service_account* service_account) { + if (service_account == nullptr) { + return; + } + gpr_free(service_account->data); + gpr_free(service_account); +} + +static const grpc_alts_credentials_options_vtable vtable = { + alts_client_options_copy, alts_client_options_destroy}; + +grpc_alts_credentials_options* grpc_alts_credentials_client_options_create() { + auto client_options = static_cast<grpc_alts_credentials_client_options*>( + gpr_zalloc(sizeof(grpc_alts_credentials_client_options))); + client_options->base.vtable = &vtable; + return &client_options->base; +} + +static grpc_alts_credentials_options* alts_client_options_copy( + const grpc_alts_credentials_options* options) { + if (options == nullptr) { + return nullptr; + } + grpc_alts_credentials_options* new_options = + grpc_alts_credentials_client_options_create(); + auto new_client_options = + reinterpret_cast<grpc_alts_credentials_client_options*>(new_options); + /* Copy target service accounts. */ + target_service_account* prev = nullptr; + auto node = + (reinterpret_cast<const grpc_alts_credentials_client_options*>(options)) + ->target_account_list_head; + while (node != nullptr) { + target_service_account* new_node = + target_service_account_create(node->data); + if (prev == nullptr) { + new_client_options->target_account_list_head = new_node; + } else { + prev->next = new_node; + } + prev = new_node; + node = node->next; + } + /* Copy rpc protocol versions. */ + grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions, + &new_options->rpc_versions); + return new_options; +} + +static void alts_client_options_destroy( + grpc_alts_credentials_options* options) { + if (options == nullptr) { + return; + } + auto* client_options = + reinterpret_cast<grpc_alts_credentials_client_options*>(options); + target_service_account* node = client_options->target_account_list_head; + while (node != nullptr) { + target_service_account* next_node = node->next; + target_service_account_destroy(node); + node = next_node; + } +} diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc new file mode 100644 index 0000000000..d428171540 --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc @@ -0,0 +1,46 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +grpc_alts_credentials_options* grpc_alts_credentials_options_copy( + const grpc_alts_credentials_options* options) { + if (options != nullptr && options->vtable != nullptr && + options->vtable->copy != nullptr) { + return options->vtable->copy(options); + } + /* An error occurred. */ + gpr_log(GPR_ERROR, + "Invalid arguments to grpc_alts_credentials_options_copy()"); + return nullptr; +} + +void grpc_alts_credentials_options_destroy( + grpc_alts_credentials_options* options) { + if (options != nullptr) { + if (options->vtable != nullptr && options->vtable->destruct != nullptr) { + options->vtable->destruct(options); + } + gpr_free(options); + } +} diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h new file mode 100644 index 0000000000..4e46d9f2de --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h @@ -0,0 +1,112 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +/** + * Main interface for ALTS credentials options. The options will contain + * information that will be passed from grpc to TSI layer such as RPC protocol + * versions. ALTS client (channel) and server credentials will have their own + * implementation of this interface. The APIs listed in this header are + * thread-compatible. + */ +typedef struct grpc_alts_credentials_options grpc_alts_credentials_options; + +/* V-table for grpc_alts_credentials_options */ +typedef struct grpc_alts_credentials_options_vtable { + grpc_alts_credentials_options* (*copy)( + const grpc_alts_credentials_options* options); + void (*destruct)(grpc_alts_credentials_options* options); +} grpc_alts_credentials_options_vtable; + +struct grpc_alts_credentials_options { + const struct grpc_alts_credentials_options_vtable* vtable; + grpc_gcp_rpc_protocol_versions rpc_versions; +}; + +typedef struct target_service_account { + struct target_service_account* next; + char* data; +} target_service_account; + +/** + * Main struct for ALTS client credentials options. The options contain a + * a list of target service accounts (if specified) used for secure naming + * check. + */ +typedef struct grpc_alts_credentials_client_options { + grpc_alts_credentials_options base; + target_service_account* target_account_list_head; +} grpc_alts_credentials_client_options; + +/** + * Main struct for ALTS server credentials options. The options currently + * do not contain any server-specific fields. + */ +typedef struct grpc_alts_credentials_server_options { + grpc_alts_credentials_options base; +} grpc_alts_credentials_server_options; + +/** + * This method performs a deep copy on grpc_alts_credentials_options instance. + * + * - options: a grpc_alts_credentials_options instance that needs to be copied. + * + * It returns a new grpc_alts_credentials_options instance on success and NULL + * on failure. + */ +grpc_alts_credentials_options* grpc_alts_credentials_options_copy( + const grpc_alts_credentials_options* options); + +/** + * This method destroys a grpc_alts_credentials_options instance by + * de-allocating all of its occupied memory. + * + * - options: a grpc_alts_credentials_options instance that needs to be + * destroyed. + */ +void grpc_alts_credentials_options_destroy( + grpc_alts_credentials_options* options); + +/* This method creates a grpc ALTS credentials client options instance. */ +grpc_alts_credentials_options* grpc_alts_credentials_client_options_create(); + +/* This method creates a grpc ALTS credentials server options instance. */ +grpc_alts_credentials_options* grpc_alts_credentials_server_options_create(); + +/** + * This method adds a target service account to grpc ALTS credentials client + * options instance. + * + * - options: grpc ALTS credentials client options instance. + * - service_account: service account of target endpoint. + * + * It returns true on success and false on failure. + */ +bool grpc_alts_credentials_client_options_add_target_service_account( + grpc_alts_credentials_client_options* options, const char* service_account); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H \ + */ diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc new file mode 100644 index 0000000000..62aa7a620a --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stdlib.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +static grpc_alts_credentials_options* alts_server_options_copy( + const grpc_alts_credentials_options* options); + +static void alts_server_options_destroy( + grpc_alts_credentials_options* options) {} + +static const grpc_alts_credentials_options_vtable vtable = { + alts_server_options_copy, alts_server_options_destroy}; + +grpc_alts_credentials_options* grpc_alts_credentials_server_options_create() { + grpc_alts_credentials_server_options* server_options = + static_cast<grpc_alts_credentials_server_options*>( + gpr_zalloc(sizeof(*server_options))); + server_options->base.vtable = &vtable; + return &server_options->base; +} + +static grpc_alts_credentials_options* alts_server_options_copy( + const grpc_alts_credentials_options* options) { + if (options == nullptr) { + return nullptr; + } + grpc_alts_credentials_options* new_options = + grpc_alts_credentials_server_options_create(); + /* Copy rpc protocol versions. */ + grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions, + &new_options->rpc_versions); + return new_options; +} diff --git a/src/core/lib/security/credentials/fake/fake_credentials.cc b/src/core/lib/security/credentials/fake/fake_credentials.cc index 46311fa122..858ab6b41b 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.cc +++ b/src/core/lib/security/credentials/fake/fake_credentials.cc @@ -32,9 +32,6 @@ /* -- Fake transport security credentials. -- */ -#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ - "grpc.fake_security.expected_targets" - static grpc_security_status fake_transport_security_create_security_connector( grpc_channel_credentials* c, grpc_call_credentials* call_creds, const char* target, const grpc_channel_args* args, diff --git a/src/core/lib/security/credentials/fake/fake_credentials.h b/src/core/lib/security/credentials/fake/fake_credentials.h index 5166e43167..e89e6e24cc 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.h +++ b/src/core/lib/security/credentials/fake/fake_credentials.h @@ -23,6 +23,9 @@ #include "src/core/lib/security/credentials/credentials.h" +#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ + "grpc.fake_security.expected_targets" + /* -- Fake transport security credentials. -- */ /* Creates a fake transport security credentials object for testing. */ diff --git a/src/core/lib/security/security_connector/alts_security_connector.cc b/src/core/lib/security/security_connector/alts_security_connector.cc new file mode 100644 index 0000000000..5ff7d7938b --- /dev/null +++ b/src/core/lib/security/security_connector/alts_security_connector.cc @@ -0,0 +1,287 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/security_connector/alts_security_connector.h" + +#include <stdbool.h> +#include <string.h> + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/security/credentials/alts/alts_credentials.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/transport/transport.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" + +typedef struct { + grpc_channel_security_connector base; + char* target_name; +} grpc_alts_channel_security_connector; + +typedef struct { + grpc_server_security_connector base; +} grpc_alts_server_security_connector; + +static void alts_channel_destroy(grpc_security_connector* sc) { + if (sc == nullptr) { + return; + } + auto c = reinterpret_cast<grpc_alts_channel_security_connector*>(sc); + grpc_call_credentials_unref(c->base.request_metadata_creds); + grpc_channel_credentials_unref(c->base.channel_creds); + gpr_free(c->target_name); + gpr_free(sc); +} + +static void alts_server_destroy(grpc_security_connector* sc) { + if (sc == nullptr) { + return; + } + auto c = reinterpret_cast<grpc_alts_server_security_connector*>(sc); + grpc_server_credentials_unref(c->base.server_creds); + gpr_free(sc); +} + +static void alts_channel_add_handshakers( + grpc_channel_security_connector* sc, + grpc_handshake_manager* handshake_manager) { + tsi_handshaker* handshaker = nullptr; + auto c = reinterpret_cast<grpc_alts_channel_security_connector*>(sc); + grpc_alts_credentials* creds = + reinterpret_cast<grpc_alts_credentials*>(c->base.channel_creds); + GPR_ASSERT(alts_tsi_handshaker_create(creds->options, c->target_name, + creds->handshaker_service_url, true, + &handshaker) == TSI_OK); + grpc_handshake_manager_add(handshake_manager, grpc_security_handshaker_create( + handshaker, &sc->base)); +} + +static void alts_server_add_handshakers( + grpc_server_security_connector* sc, + grpc_handshake_manager* handshake_manager) { + tsi_handshaker* handshaker = nullptr; + auto c = reinterpret_cast<grpc_alts_server_security_connector*>(sc); + grpc_alts_server_credentials* creds = + reinterpret_cast<grpc_alts_server_credentials*>(c->base.server_creds); + GPR_ASSERT(alts_tsi_handshaker_create(creds->options, nullptr, + creds->handshaker_service_url, false, + &handshaker) == TSI_OK); + grpc_handshake_manager_add(handshake_manager, grpc_security_handshaker_create( + handshaker, &sc->base)); +} + +static void alts_set_rpc_protocol_versions( + grpc_gcp_rpc_protocol_versions* rpc_versions) { + grpc_gcp_rpc_protocol_versions_set_max(rpc_versions, + GRPC_PROTOCOL_VERSION_MAX_MAJOR, + GRPC_PROTOCOL_VERSION_MAX_MINOR); + grpc_gcp_rpc_protocol_versions_set_min(rpc_versions, + GRPC_PROTOCOL_VERSION_MIN_MAJOR, + GRPC_PROTOCOL_VERSION_MIN_MINOR); +} + +namespace grpc_core { +namespace internal { + +grpc_security_status grpc_alts_auth_context_from_tsi_peer( + const tsi_peer* peer, grpc_auth_context** ctx) { + if (peer == nullptr || ctx == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to grpc_alts_auth_context_from_tsi_peer()"); + return GRPC_SECURITY_ERROR; + } + *ctx = nullptr; + /* Validate certificate type. */ + const tsi_peer_property* cert_type_prop = + tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY); + if (cert_type_prop == nullptr || + strncmp(cert_type_prop->value.data, TSI_ALTS_CERTIFICATE_TYPE, + cert_type_prop->value.length) != 0) { + gpr_log(GPR_ERROR, "Invalid or missing certificate type property."); + return GRPC_SECURITY_ERROR; + } + /* Validate RPC protocol versions. */ + const tsi_peer_property* rpc_versions_prop = + tsi_peer_get_property_by_name(peer, TSI_ALTS_RPC_VERSIONS); + if (rpc_versions_prop == nullptr) { + gpr_log(GPR_ERROR, "Missing rpc protocol versions property."); + return GRPC_SECURITY_ERROR; + } + grpc_gcp_rpc_protocol_versions local_versions, peer_versions; + alts_set_rpc_protocol_versions(&local_versions); + grpc_slice slice = grpc_slice_from_copied_buffer( + rpc_versions_prop->value.data, rpc_versions_prop->value.length); + bool decode_result = + grpc_gcp_rpc_protocol_versions_decode(slice, &peer_versions); + grpc_slice_unref(slice); + if (!decode_result) { + gpr_log(GPR_ERROR, "Invalid peer rpc protocol versions."); + return GRPC_SECURITY_ERROR; + } + /* TODO: Pass highest common rpc protocol version to grpc caller. */ + bool check_result = grpc_gcp_rpc_protocol_versions_check( + &local_versions, &peer_versions, nullptr); + if (!check_result) { + gpr_log(GPR_ERROR, "Mismatch of local and peer rpc protocol versions."); + return GRPC_SECURITY_ERROR; + } + /* Create auth context. */ + *ctx = grpc_auth_context_create(nullptr); + grpc_auth_context_add_cstring_property( + *ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_ALTS_TRANSPORT_SECURITY_TYPE); + size_t i = 0; + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property* tsi_prop = &peer->properties[i]; + /* Add service account to auth context. */ + if (strcmp(tsi_prop->name, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 0) { + grpc_auth_context_add_property( + *ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, tsi_prop->value.data, + tsi_prop->value.length); + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + *ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 1); + } + } + if (!grpc_auth_context_peer_is_authenticated(*ctx)) { + gpr_log(GPR_ERROR, "Invalid unauthenticated peer."); + GRPC_AUTH_CONTEXT_UNREF(*ctx, "test"); + *ctx = nullptr; + return GRPC_SECURITY_ERROR; + } + return GRPC_SECURITY_OK; +} + +} // namespace internal +} // namespace grpc_core + +static void alts_check_peer(grpc_security_connector* sc, tsi_peer peer, + grpc_auth_context** auth_context, + grpc_closure* on_peer_checked) { + grpc_security_status status; + status = grpc_core::internal::grpc_alts_auth_context_from_tsi_peer( + &peer, auth_context); + tsi_peer_destruct(&peer); + grpc_error* error = + status == GRPC_SECURITY_OK + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Could not get ALTS auth context from TSI peer"); + GRPC_CLOSURE_SCHED(on_peer_checked, error); +} + +static int alts_channel_cmp(grpc_security_connector* sc1, + grpc_security_connector* sc2) { + grpc_alts_channel_security_connector* c1 = + reinterpret_cast<grpc_alts_channel_security_connector*>(sc1); + grpc_alts_channel_security_connector* c2 = + reinterpret_cast<grpc_alts_channel_security_connector*>(sc2); + int c = grpc_channel_security_connector_cmp(&c1->base, &c2->base); + if (c != 0) return c; + return strcmp(c1->target_name, c2->target_name); +} + +static int alts_server_cmp(grpc_security_connector* sc1, + grpc_security_connector* sc2) { + grpc_alts_server_security_connector* c1 = + reinterpret_cast<grpc_alts_server_security_connector*>(sc1); + grpc_alts_server_security_connector* c2 = + reinterpret_cast<grpc_alts_server_security_connector*>(sc2); + return grpc_server_security_connector_cmp(&c1->base, &c2->base); +} + +static grpc_security_connector_vtable alts_channel_vtable = { + alts_channel_destroy, alts_check_peer, alts_channel_cmp}; + +static grpc_security_connector_vtable alts_server_vtable = { + alts_server_destroy, alts_check_peer, alts_server_cmp}; + +static bool alts_check_call_host(grpc_channel_security_connector* sc, + const char* host, + grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) { + grpc_alts_channel_security_connector* alts_sc = + reinterpret_cast<grpc_alts_channel_security_connector*>(sc); + if (host == nullptr || alts_sc == nullptr || + strcmp(host, alts_sc->target_name) != 0) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "ALTS call host does not match target name"); + } + return true; +} + +static void alts_cancel_check_call_host(grpc_channel_security_connector* sc, + grpc_closure* on_call_host_checked, + grpc_error* error) { + GRPC_ERROR_UNREF(error); +} + +grpc_security_status grpc_alts_channel_security_connector_create( + grpc_channel_credentials* channel_creds, + grpc_call_credentials* request_metadata_creds, const char* target_name, + grpc_channel_security_connector** sc) { + if (channel_creds == nullptr || sc == nullptr || target_name == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_alts_channel_security_connector_create()"); + return GRPC_SECURITY_ERROR; + } + auto c = static_cast<grpc_alts_channel_security_connector*>( + gpr_zalloc(sizeof(grpc_alts_channel_security_connector))); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &alts_channel_vtable; + c->base.add_handshakers = alts_channel_add_handshakers; + c->base.channel_creds = grpc_channel_credentials_ref(channel_creds); + c->base.request_metadata_creds = + grpc_call_credentials_ref(request_metadata_creds); + c->base.check_call_host = alts_check_call_host; + c->base.cancel_check_call_host = alts_cancel_check_call_host; + grpc_alts_credentials* creds = + reinterpret_cast<grpc_alts_credentials*>(c->base.channel_creds); + alts_set_rpc_protocol_versions(&creds->options->rpc_versions); + c->target_name = gpr_strdup(target_name); + *sc = &c->base; + return GRPC_SECURITY_OK; +} + +grpc_security_status grpc_alts_server_security_connector_create( + grpc_server_credentials* server_creds, + grpc_server_security_connector** sc) { + if (server_creds == nullptr || sc == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_alts_server_security_connector_create()"); + return GRPC_SECURITY_ERROR; + } + auto c = static_cast<grpc_alts_server_security_connector*>( + gpr_zalloc(sizeof(grpc_alts_server_security_connector))); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &alts_server_vtable; + c->base.server_creds = grpc_server_credentials_ref(server_creds); + c->base.add_handshakers = alts_server_add_handshakers; + grpc_alts_server_credentials* creds = + reinterpret_cast<grpc_alts_server_credentials*>(c->base.server_creds); + alts_set_rpc_protocol_versions(&creds->options->rpc_versions); + *sc = &c->base; + return GRPC_SECURITY_OK; +} diff --git a/src/core/lib/security/security_connector/alts_security_connector.h b/src/core/lib/security/security_connector/alts_security_connector.h new file mode 100644 index 0000000000..e7e4cffe2a --- /dev/null +++ b/src/core/lib/security/security_connector/alts_security_connector.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" + +#define GRPC_ALTS_TRANSPORT_SECURITY_TYPE "alts" + +/** + * This method creates an ALTS channel security connector. + * + * - channel_creds: channel credential instance. + * - request_metadata_creds: credential object which will be sent with each + * request. This parameter can be nullptr. + * - target_name: the name of the endpoint that the channel is connecting to. + * - sc: address of ALTS channel security connector instance to be returned from + * the method. + * + * It returns GRPC_SECURITY_OK on success, and an error stauts code on failure. + */ +grpc_security_status grpc_alts_channel_security_connector_create( + grpc_channel_credentials* channel_creds, + grpc_call_credentials* request_metadata_creds, const char* target_name, + grpc_channel_security_connector** sc); + +/** + * This method creates an ALTS server security connector. + * + * - server_creds: server credential instance. + * - sc: address of ALTS server security connector instance to be returned from + * the method. + * + * It returns GRPC_SECURITY_OK on success, and an error status code on failure. + */ +grpc_security_status grpc_alts_server_security_connector_create( + grpc_server_credentials* server_creds, grpc_server_security_connector** sc); + +namespace grpc_core { +namespace internal { + +/* Exposed only for testing. */ +grpc_security_status grpc_alts_auth_context_from_tsi_peer( + const tsi_peer* peer, grpc_auth_context** ctx); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H \ + */ diff --git a/src/core/lib/security/security_connector/security_connector.cc b/src/core/lib/security/security_connector/security_connector.cc index b01fd6f769..3cc151bec7 100644 --- a/src/core/lib/security/security_connector/security_connector.cc +++ b/src/core/lib/security/security_connector/security_connector.cc @@ -39,9 +39,9 @@ #include "src/core/lib/security/credentials/credentials.h" #include "src/core/lib/security/credentials/fake/fake_credentials.h" #include "src/core/lib/security/credentials/ssl/ssl_credentials.h" -#include "src/core/lib/security/transport/lb_targets_info.h" #include "src/core/lib/security/transport/secure_endpoint.h" #include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/security/transport/target_authority_table.h" #include "src/core/tsi/fake_transport_security.h" #include "src/core/tsi/ssl_transport_security.h" #include "src/core/tsi/transport_security_adapter.h" @@ -463,6 +463,15 @@ static bool fake_channel_check_call_host(grpc_channel_security_connector* sc, grpc_auth_context* auth_context, grpc_closure* on_call_host_checked, grpc_error** error) { + grpc_fake_channel_security_connector* c = + reinterpret_cast<grpc_fake_channel_security_connector*>(sc); + if (c->is_lb_channel) { + // TODO(dgq): verify that the host (ie, authority header) matches that of + // the LB, as opposed to that of the backends. + } else { + // TODO(dgq): verify that the host (ie, authority header) matches that of + // the backend, not the LB's. + } return true; } @@ -514,7 +523,7 @@ grpc_channel_security_connector* grpc_fake_channel_security_connector_create( c->target = gpr_strdup(target); const char* expected_targets = grpc_fake_transport_get_expected_targets(args); c->expected_targets = gpr_strdup(expected_targets); - c->is_lb_channel = (grpc_lb_targets_info_find_in_args(args) != nullptr); + c->is_lb_channel = grpc_core::FindTargetAuthorityTableInArgs(args) != nullptr; return &c->base; } diff --git a/src/core/lib/security/transport/client_auth_filter.cc b/src/core/lib/security/transport/client_auth_filter.cc index d6ca8ee8f8..048e390a71 100644 --- a/src/core/lib/security/transport/client_auth_filter.cc +++ b/src/core/lib/security/transport/client_auth_filter.cc @@ -45,8 +45,6 @@ struct call_data { grpc_call_stack* owning_call; grpc_call_combiner* call_combiner; grpc_call_credentials* creds; - bool have_host; - bool have_method; grpc_slice host; grpc_slice method; /* pollset{_set} bound to this call; if we need to make external @@ -294,27 +292,15 @@ static void auth_start_transport_stream_op_batch( } if (batch->send_initial_metadata) { - for (grpc_linked_mdelem* l = batch->payload->send_initial_metadata - .send_initial_metadata->list.head; - l != nullptr; l = l->next) { - grpc_mdelem md = l->md; - /* Pointer comparison is OK for md_elems created from the same context. - */ - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) { - if (calld->have_host) { - grpc_slice_unref_internal(calld->host); - } - calld->host = grpc_slice_ref_internal(GRPC_MDVALUE(md)); - calld->have_host = true; - } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) { - if (calld->have_method) { - grpc_slice_unref_internal(calld->method); - } - calld->method = grpc_slice_ref_internal(GRPC_MDVALUE(md)); - calld->have_method = true; - } + grpc_metadata_batch* metadata = + batch->payload->send_initial_metadata.send_initial_metadata; + if (metadata->idx.named.path != nullptr) { + calld->method = + grpc_slice_ref_internal(GRPC_MDVALUE(metadata->idx.named.path->md)); } - if (calld->have_host) { + if (metadata->idx.named.authority != nullptr) { + calld->host = grpc_slice_ref_internal( + GRPC_MDVALUE(metadata->idx.named.authority->md)); batch->handler_private.extra_arg = elem; GRPC_CALL_STACK_REF(calld->owning_call, "check_call_host"); GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch, @@ -351,6 +337,8 @@ static grpc_error* init_call_elem(grpc_call_element* elem, call_data* calld = static_cast<call_data*>(elem->call_data); calld->owning_call = args->call_stack; calld->call_combiner = args->call_combiner; + calld->host = grpc_empty_slice(); + calld->method = grpc_empty_slice(); return GRPC_ERROR_NONE; } @@ -367,12 +355,8 @@ static void destroy_call_elem(grpc_call_element* elem, call_data* calld = static_cast<call_data*>(elem->call_data); grpc_credentials_mdelem_array_destroy(&calld->md_array); grpc_call_credentials_unref(calld->creds); - if (calld->have_host) { - grpc_slice_unref_internal(calld->host); - } - if (calld->have_method) { - grpc_slice_unref_internal(calld->method); - } + grpc_slice_unref_internal(calld->host); + grpc_slice_unref_internal(calld->method); grpc_auth_metadata_context_reset(&calld->auth_md_context); } diff --git a/src/core/lib/security/transport/lb_targets_info.cc b/src/core/lib/security/transport/lb_targets_info.cc deleted file mode 100644 index 155a91e556..0000000000 --- a/src/core/lib/security/transport/lb_targets_info.cc +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include <grpc/support/port_platform.h> - -#include <grpc/support/log.h> - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/transport/lb_targets_info.h" - -/* Channel arg key for the mapping of LB server addresses to their names for - * secure naming purposes. */ -#define GRPC_ARG_LB_SECURE_NAMING_MAP "grpc.lb_secure_naming_map" - -static void* targets_info_copy(void* p) { - return grpc_slice_hash_table_ref(static_cast<grpc_slice_hash_table*>(p)); -} -static void targets_info_destroy(void* p) { - grpc_slice_hash_table_unref(static_cast<grpc_slice_hash_table*>(p)); -} -static int targets_info_cmp(void* a, void* b) { - return grpc_slice_hash_table_cmp( - static_cast<const grpc_slice_hash_table*>(a), - static_cast<const grpc_slice_hash_table*>(b)); -} -static const grpc_arg_pointer_vtable server_to_balancer_names_vtable = { - targets_info_copy, targets_info_destroy, targets_info_cmp}; - -grpc_arg grpc_lb_targets_info_create_channel_arg( - grpc_slice_hash_table* targets_info) { - return grpc_channel_arg_pointer_create((char*)GRPC_ARG_LB_SECURE_NAMING_MAP, - targets_info, - &server_to_balancer_names_vtable); -} - -grpc_slice_hash_table* grpc_lb_targets_info_find_in_args( - const grpc_channel_args* args) { - const grpc_arg* targets_info_arg = - grpc_channel_args_find(args, GRPC_ARG_LB_SECURE_NAMING_MAP); - if (targets_info_arg != nullptr) { - GPR_ASSERT(targets_info_arg->type == GRPC_ARG_POINTER); - return static_cast<grpc_slice_hash_table*>( - targets_info_arg->value.pointer.p); - } - return nullptr; -} diff --git a/src/core/lib/security/transport/target_authority_table.cc b/src/core/lib/security/transport/target_authority_table.cc new file mode 100644 index 0000000000..1eeb557f6a --- /dev/null +++ b/src/core/lib/security/transport/target_authority_table.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/log.h> + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/transport/target_authority_table.h" + +// Channel arg key for the mapping of target addresses to their authorities. +#define GRPC_ARG_TARGET_AUTHORITY_TABLE "grpc.target_authority_table" + +namespace grpc_core { +namespace { + +void* target_authority_table_copy(void* p) { + TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p); + // TODO(roth): When channel_args are converted to C++, pass the + // RefCountedPtr<> directly instead of managing the ref manually. + table->Ref().release(); + return p; +} +void target_authority_table_destroy(void* p) { + TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p); + table->Unref(); +} +int target_authority_table_cmp(void* a, void* b) { + return TargetAuthorityTable::Cmp( + *static_cast<const TargetAuthorityTable*>(a), + *static_cast<const TargetAuthorityTable*>(b)); +} +const grpc_arg_pointer_vtable target_authority_table_arg_vtable = { + target_authority_table_copy, target_authority_table_destroy, + target_authority_table_cmp}; + +} // namespace + +grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_TARGET_AUTHORITY_TABLE, + table, + &target_authority_table_arg_vtable); +} + +TargetAuthorityTable* FindTargetAuthorityTableInArgs( + const grpc_channel_args* args) { + const grpc_arg* arg = + grpc_channel_args_find(args, GRPC_ARG_TARGET_AUTHORITY_TABLE); + if (arg != nullptr) { + if (arg->type == GRPC_ARG_POINTER) { + return static_cast<TargetAuthorityTable*>(arg->value.pointer.p); + } else { + gpr_log(GPR_ERROR, "value of " GRPC_ARG_TARGET_AUTHORITY_TABLE + " channel arg was not pointer type; ignoring"); + } + } + return nullptr; +} + +} // namespace grpc_core diff --git a/src/core/lib/security/transport/lb_targets_info.h b/src/core/lib/security/transport/target_authority_table.h index 7e816c5222..a2e7dc6ac2 100644 --- a/src/core/lib/security/transport/lb_targets_info.h +++ b/src/core/lib/security/transport/target_authority_table.h @@ -16,19 +16,25 @@ * */ -#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H -#define GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H #include <grpc/support/port_platform.h> #include "src/core/lib/slice/slice_hash_table.h" -/** Return a channel argument containing \a targets_info. */ -grpc_arg grpc_lb_targets_info_create_channel_arg( - grpc_slice_hash_table* targets_info); +namespace grpc_core { -/** Return the instance of targets info in \a args or NULL */ -grpc_slice_hash_table* grpc_lb_targets_info_find_in_args( +/// A hash table mapping target addresses to authorities. +typedef SliceHashTable<UniquePtr<char>> TargetAuthorityTable; + +/// Returns a channel argument containing \a table. +grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table); + +/// Returns the target authority table from \a args or nullptr. +TargetAuthorityTable* FindTargetAuthorityTableInArgs( const grpc_channel_args* args); -#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H */ +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H */ diff --git a/src/core/lib/slice/slice_hash_table.cc b/src/core/lib/slice/slice_hash_table.cc deleted file mode 100644 index 9e32321636..0000000000 --- a/src/core/lib/slice/slice_hash_table.cc +++ /dev/null @@ -1,147 +0,0 @@ -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include <grpc/support/port_platform.h> - -#include "src/core/lib/slice/slice_hash_table.h" - -#include <stdbool.h> -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/transport/metadata.h" - -struct grpc_slice_hash_table { - gpr_refcount refs; - void (*destroy_value)(void* value); - int (*value_cmp)(void* a, void* b); - size_t size; - size_t max_num_probes; - grpc_slice_hash_table_entry* entries; -}; - -static bool is_empty(grpc_slice_hash_table_entry* entry) { - return entry->value == nullptr; -} - -static void grpc_slice_hash_table_add(grpc_slice_hash_table* table, - grpc_slice key, void* value) { - GPR_ASSERT(value != nullptr); - const size_t hash = grpc_slice_hash(key); - for (size_t offset = 0; offset < table->size; ++offset) { - const size_t idx = (hash + offset) % table->size; - if (is_empty(&table->entries[idx])) { - table->entries[idx].key = key; - table->entries[idx].value = value; - // Keep track of the maximum number of probes needed, since this - // provides an upper bound for lookups. - if (offset > table->max_num_probes) table->max_num_probes = offset; - return; - } - } - GPR_ASSERT(false); // Table should never be full. -} - -grpc_slice_hash_table* grpc_slice_hash_table_create( - size_t num_entries, grpc_slice_hash_table_entry* entries, - void (*destroy_value)(void* value), int (*value_cmp)(void* a, void* b)) { - grpc_slice_hash_table* table = - static_cast<grpc_slice_hash_table*>(gpr_zalloc(sizeof(*table))); - gpr_ref_init(&table->refs, 1); - table->destroy_value = destroy_value; - table->value_cmp = value_cmp; - // Keep load factor low to improve performance of lookups. - table->size = num_entries * 2; - const size_t entry_size = sizeof(grpc_slice_hash_table_entry) * table->size; - table->entries = - static_cast<grpc_slice_hash_table_entry*>(gpr_zalloc(entry_size)); - for (size_t i = 0; i < num_entries; ++i) { - grpc_slice_hash_table_entry* entry = &entries[i]; - grpc_slice_hash_table_add(table, entry->key, entry->value); - } - return table; -} - -grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table) { - if (table != nullptr) gpr_ref(&table->refs); - return table; -} - -void grpc_slice_hash_table_unref(grpc_slice_hash_table* table) { - if (table != nullptr && gpr_unref(&table->refs)) { - for (size_t i = 0; i < table->size; ++i) { - grpc_slice_hash_table_entry* entry = &table->entries[i]; - if (!is_empty(entry)) { - grpc_slice_unref_internal(entry->key); - table->destroy_value(entry->value); - } - } - gpr_free(table->entries); - gpr_free(table); - } -} - -void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table, - const grpc_slice key) { - const size_t hash = grpc_slice_hash(key); - // We cap the number of probes at the max number recorded when - // populating the table. - for (size_t offset = 0; offset <= table->max_num_probes; ++offset) { - const size_t idx = (hash + offset) % table->size; - if (is_empty(&table->entries[idx])) break; - if (grpc_slice_eq(table->entries[idx].key, key)) { - return table->entries[idx].value; - } - } - return nullptr; // Not found. -} - -static int pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); } -int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a, - const grpc_slice_hash_table* b) { - int (*const value_cmp_fn_a)(void* a, void* b) = - a->value_cmp != nullptr ? a->value_cmp : pointer_cmp; - int (*const value_cmp_fn_b)(void* a, void* b) = - b->value_cmp != nullptr ? b->value_cmp : pointer_cmp; - // Compare value_fns - const int value_fns_cmp = - GPR_ICMP((void*)value_cmp_fn_a, (void*)value_cmp_fn_b); - if (value_fns_cmp != 0) return value_fns_cmp; - // Compare sizes - if (a->size < b->size) return -1; - if (a->size > b->size) return 1; - // Compare rows. - for (size_t i = 0; i < a->size; ++i) { - if (is_empty(&a->entries[i])) { - if (!is_empty(&b->entries[i])) { - return -1; // a empty but b non-empty - } - continue; // both empty, no need to check key or value - } else if (is_empty(&b->entries[i])) { - return 1; // a non-empty but b empty - } - // neither entry is empty - const int key_cmp = grpc_slice_cmp(a->entries[i].key, b->entries[i].key); - if (key_cmp != 0) return key_cmp; - const int value_cmp = - value_cmp_fn_a(a->entries[i].value, b->entries[i].value); - if (value_cmp != 0) return value_cmp; - } - return 0; -} diff --git a/src/core/lib/slice/slice_hash_table.h b/src/core/lib/slice/slice_hash_table.h index 819bb3b5bc..fbe9cc58e8 100644 --- a/src/core/lib/slice/slice_hash_table.h +++ b/src/core/lib/slice/slice_hash_table.h @@ -19,52 +19,183 @@ #include <grpc/support/port_platform.h> -#include "src/core/lib/transport/metadata.h" +#include <string.h> -/** Hash table implementation. - * - * This implementation uses open addressing - * (https://en.wikipedia.org/wiki/Open_addressing) with linear - * probing (https://en.wikipedia.org/wiki/Linear_probing). - * - * The keys are \a grpc_slice objects. The values are arbitrary pointers - * with a common destroy function. - * - * Hash tables are intentionally immutable, to avoid the need for locking. - */ +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/slice/slice_internal.h" + +/// Hash table implementation. +/// +/// This implementation uses open addressing +/// (https://en.wikipedia.org/wiki/Open_addressing) with linear +/// probing (https://en.wikipedia.org/wiki/Linear_probing). +/// +/// The keys are \a grpc_slice objects. The values can be any type. +/// +/// Hash tables are intentionally immutable, to avoid the need for locking. + +namespace grpc_core { + +template <typename T> +class SliceHashTable : public RefCounted<SliceHashTable<T>> { + public: + struct Entry { + grpc_slice key; + T value; + bool is_set; + }; + + // Function for comparing values. + // TODO(roth): Eliminate this and the Cmp() method from this API once + // grpc_channel_args is redesigned to require that keys are unique. + typedef int (*ValueCmp)(const T&, const T&); + + /// Creates a new hash table containing \a entries, which is an array + /// of length \a num_entries. Takes ownership of all keys and values in \a + /// entries. If not null, \a value_cmp will be used to compare values in + /// the context of \a Cmp(). If null, raw pointer (\a GPR_ICMP) comparison + /// will be used. + static RefCountedPtr<SliceHashTable> Create(size_t num_entries, + Entry* entries, + ValueCmp value_cmp); + + /// Returns the value from the table associated with \a key. + /// Returns null if \a key is not found. + const T* Get(const grpc_slice& key) const; + + /// Compares \a a vs. \a b. + /// A table is considered "smaller" (resp. "greater") if: + /// - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1), + /// - else, it contains fewer (resp. more) entries, + /// - else, if strcmp(a_key, b_key) < 1 (resp. > 1), + /// - else, if value_cmp(a_value, b_value) < 1 (resp. > 1). + static int Cmp(const SliceHashTable& a, const SliceHashTable& b); + + private: + // So New() can call our private ctor. + template <typename T2, typename... Args> + friend T2* New(Args&&... args); + + SliceHashTable(size_t num_entries, Entry* entries, ValueCmp value_cmp); + virtual ~SliceHashTable(); + + void Add(grpc_slice key, T& value); + + // Default value comparison function, if none specified by caller. + static int DefaultValueCmp(const T& a, const T& b) { return GPR_ICMP(a, b); } + + const ValueCmp value_cmp_; + const size_t size_; + size_t max_num_probes_; + Entry* entries_; +}; + +// +// implementation -- no user-serviceable parts below +// + +template <typename T> +RefCountedPtr<SliceHashTable<T>> SliceHashTable<T>::Create(size_t num_entries, + Entry* entries, + ValueCmp value_cmp) { + return MakeRefCounted<SliceHashTable<T>>(num_entries, entries, value_cmp); +} + +template <typename T> +SliceHashTable<T>::SliceHashTable(size_t num_entries, Entry* entries, + ValueCmp value_cmp) + : value_cmp_(value_cmp), + // Keep load factor low to improve performance of lookups. + size_(num_entries * 2), + max_num_probes_(0) { + entries_ = static_cast<Entry*>(gpr_zalloc(sizeof(Entry) * size_)); + for (size_t i = 0; i < num_entries; ++i) { + Entry* entry = &entries[i]; + Add(entry->key, entry->value); + } +} + +template <typename T> +SliceHashTable<T>::~SliceHashTable() { + for (size_t i = 0; i < size_; ++i) { + Entry& entry = entries_[i]; + if (entry.is_set) { + grpc_slice_unref_internal(entry.key); + entry.value.~T(); + } + } + gpr_free(entries_); +} + +template <typename T> +void SliceHashTable<T>::Add(grpc_slice key, T& value) { + const size_t hash = grpc_slice_hash(key); + for (size_t offset = 0; offset < size_; ++offset) { + const size_t idx = (hash + offset) % size_; + if (!entries_[idx].is_set) { + entries_[idx].is_set = true; + entries_[idx].key = key; + entries_[idx].value = std::move(value); + // Keep track of the maximum number of probes needed, since this + // provides an upper bound for lookups. + if (offset > max_num_probes_) max_num_probes_ = offset; + return; + } + } + GPR_ASSERT(false); // Table should never be full. +} + +template <typename T> +const T* SliceHashTable<T>::Get(const grpc_slice& key) const { + const size_t hash = grpc_slice_hash(key); + // We cap the number of probes at the max number recorded when + // populating the table. + for (size_t offset = 0; offset <= max_num_probes_; ++offset) { + const size_t idx = (hash + offset) % size_; + if (!entries_[idx].is_set) break; + if (grpc_slice_eq(entries_[idx].key, key)) { + return &entries_[idx].value; + } + } + return nullptr; // Not found. +} + +template <typename T> +int SliceHashTable<T>::Cmp(const SliceHashTable& a, const SliceHashTable& b) { + ValueCmp value_cmp_a = + a.value_cmp_ != nullptr ? a.value_cmp_ : DefaultValueCmp; + ValueCmp value_cmp_b = + b.value_cmp_ != nullptr ? b.value_cmp_ : DefaultValueCmp; + // Compare value_fns + const int value_fns_cmp = GPR_ICMP((void*)value_cmp_a, (void*)value_cmp_b); + if (value_fns_cmp != 0) return value_fns_cmp; + // Compare sizes + if (a.size_ < b.size_) return -1; + if (a.size_ > b.size_) return 1; + // Compare rows. + for (size_t i = 0; i < a.size_; ++i) { + if (!a.entries_[i].is_set) { + if (b.entries_[i].is_set) { + return -1; // a empty but b non-empty + } + continue; // both empty, no need to check key or value + } else if (!b.entries_[i].is_set) { + return 1; // a non-empty but b empty + } + // neither entry is empty + const int key_cmp = grpc_slice_cmp(a.entries_[i].key, b.entries_[i].key); + if (key_cmp != 0) return key_cmp; + const int value_cmp = value_cmp_a(a.entries_[i].value, b.entries_[i].value); + if (value_cmp != 0) return value_cmp; + } + return 0; +} -typedef struct grpc_slice_hash_table grpc_slice_hash_table; - -typedef struct grpc_slice_hash_table_entry { - grpc_slice key; - void* value; /* Must not be NULL. */ -} grpc_slice_hash_table_entry; - -/** Creates a new hash table of containing \a entries, which is an array - of length \a num_entries. Takes ownership of all keys and values in \a - entries. Values will be cleaned up via \a destroy_value(). If not NULL, \a - value_cmp will be used to compare values in the context of \a - grpc_slice_hash_table_cmp. If NULL, raw pointer (\a GPR_ICMP) comparison - will be used. */ -grpc_slice_hash_table* grpc_slice_hash_table_create( - size_t num_entries, grpc_slice_hash_table_entry* entries, - void (*destroy_value)(void* value), int (*value_cmp)(void* a, void* b)); - -grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table); -void grpc_slice_hash_table_unref(grpc_slice_hash_table* table); - -/** Returns the value from \a table associated with \a key. - Returns NULL if \a key is not found. */ -void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table, - const grpc_slice key); - -/** Compares \a a vs. \a b. - * A table is considered "smaller" (resp. "greater") if: - * - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1), - * - else, it contains fewer (resp. more) entries, - * - else, if strcmp(a_key, b_key) < 1 (resp. > 1), - * - else, if value_cmp(a_value, b_value) < 1 (resp. > 1). */ -int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a, - const grpc_slice_hash_table* b); +} // namespace grpc_core #endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */ diff --git a/src/core/lib/slice/slice_weak_hash_table.h b/src/core/lib/slice/slice_weak_hash_table.h new file mode 100644 index 0000000000..9d0ddfc2d2 --- /dev/null +++ b/src/core/lib/slice/slice_weak_hash_table.h @@ -0,0 +1,105 @@ +/* + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H +#define GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/slice/slice_internal.h" + +/// Weak hash table implementation. +/// +/// This entries in this table are weak: an entry may be removed at any time due +/// to a number of reasons: memory pressure, hash collisions, etc. +/// +/// The keys are \a grpc_slice objects. The values are of arbitrary type. +/// +/// This class is thread unsafe. It's the caller's responsibility to ensure +/// proper locking when accessing its methods. + +namespace grpc_core { + +template <typename T, size_t Size> +class SliceWeakHashTable : public RefCounted<SliceWeakHashTable<T, Size>> { + public: + /// Creates a new table of at most \a size entries. + static RefCountedPtr<SliceWeakHashTable> Create() { + return MakeRefCounted<SliceWeakHashTable<T, Size>>(); + } + + /// Add a mapping from \a key to \a value, taking ownership of \a key. This + /// operation will always succeed. It may discard older entries. + void Add(grpc_slice key, T value) { + const size_t idx = grpc_slice_hash(key) % Size; + entries_[idx].Set(key, std::move(value)); + return; + } + + /// Returns the value from the table associated with / \a key or null if not + /// found. + const T* Get(const grpc_slice key) const { + const size_t idx = grpc_slice_hash(key) % Size; + const auto& entry = entries_[idx]; + return grpc_slice_eq(entry.key(), key) ? entry.value() : nullptr; + } + + private: + // So New() can call our private ctor. + template <typename T2, typename... Args> + friend T2* New(Args&&... args); + + SliceWeakHashTable() = default; + ~SliceWeakHashTable() = default; + + /// The type of the table "rows". + class Entry { + public: + Entry() = default; + ~Entry() { + if (is_set_) grpc_slice_unref_internal(key_); + } + grpc_slice key() const { return key_; } + + /// Return the entry's value, or null if unset. + const T* value() const { + if (!is_set_) return nullptr; + return &value_; + } + + /// Set the \a key and \a value (which is moved) for the entry. + void Set(grpc_slice key, T&& value) { + if (is_set_) grpc_slice_unref_internal(key_); + key_ = key; + value_ = std::move(value); + is_set_ = true; + } + + private: + grpc_slice key_; + T value_; + bool is_set_ = false; + }; + + Entry entries_[Size]; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H */ diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 3df745652a..c683cc02de 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -37,6 +37,7 @@ #include "src/core/lib/gpr/arena.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" @@ -50,6 +51,7 @@ #include "src/core/lib/transport/error_utils.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_metadata.h" #include "src/core/lib/transport/transport.h" /** The maximum number of concurrent batches possible. @@ -220,9 +222,9 @@ struct grpc_call { int send_extra_metadata_count; grpc_millis send_deadline; - grpc_slice_buffer_stream sending_stream; + grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sending_stream; - grpc_byte_stream* receiving_stream; + grpc_core::OrphanablePtr<grpc_core::ByteStream> receiving_stream; grpc_byte_buffer** receiving_buffer; grpc_slice receiving_slice; grpc_closure receiving_slice_ready; @@ -378,7 +380,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, bool immediately_cancel = false; if (args->parent != nullptr) { - child_call* cc = call->child = + call->child = static_cast<child_call*>(gpr_arena_alloc(arena, sizeof(child_call))); call->child->parent = args->parent; @@ -386,10 +388,6 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, GPR_ASSERT(call->is_client); GPR_ASSERT(!args->parent->is_client); - parent_call* pc = get_or_create_parent_call(args->parent); - - gpr_mu_lock(&pc->child_list_mu); - if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) { send_deadline = GPR_MIN(send_deadline, args->parent->send_deadline); } @@ -417,18 +415,6 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, immediately_cancel = true; } } - - if (pc->first_child == nullptr) { - pc->first_child = call; - cc->sibling_next = cc->sibling_prev = call; - } else { - cc->sibling_next = pc->first_child; - cc->sibling_prev = pc->first_child->child->sibling_prev; - cc->sibling_next->child->sibling_prev = - cc->sibling_prev->child->sibling_next = call; - } - - gpr_mu_unlock(&pc->child_list_mu); } call->send_deadline = send_deadline; @@ -445,6 +431,22 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, &call->call_combiner}; add_init_error(&error, grpc_call_stack_init(channel_stack, 1, destroy_call, call, &call_args)); + // Publish this call to parent only after the call stack has been initialized. + if (args->parent != nullptr) { + child_call* cc = call->child; + parent_call* pc = get_or_create_parent_call(args->parent); + gpr_mu_lock(&pc->child_list_mu); + if (pc->first_child == nullptr) { + pc->first_child = call; + cc->sibling_next = cc->sibling_prev = call; + } else { + cc->sibling_next = pc->first_child; + cc->sibling_prev = pc->first_child->child->sibling_prev; + cc->sibling_next->child->sibling_prev = + cc->sibling_prev->child->sibling_next = call; + } + gpr_mu_unlock(&pc->child_list_mu); + } if (error != GRPC_ERROR_NONE) { cancel_with_error(call, STATUS_FROM_SURFACE, GRPC_ERROR_REF(error)); } @@ -521,9 +523,7 @@ static void destroy_call(void* call, grpc_error* error) { grpc_metadata_batch_destroy( &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); } - if (c->receiving_stream != nullptr) { - grpc_byte_stream_destroy(c->receiving_stream); - } + c->receiving_stream.reset(); parent_call* pc = get_parent_call(c); if (pc != nullptr) { gpr_mu_destroy(&pc->child_list_mu); @@ -976,32 +976,6 @@ static int prepare_application_metadata(grpc_call* call, int count, return 1; } -/* we offset status by a small amount when storing it into transport metadata - as metadata cannot store a 0 value (which is used as OK for grpc_status_codes - */ -#define STATUS_OFFSET 1 -static void destroy_status(void* ignored) {} - -static uint32_t decode_status(grpc_mdelem md) { - uint32_t status; - void* user_data; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) return 0; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) return 1; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) return 2; - user_data = grpc_mdelem_get_user_data(md, destroy_status); - if (user_data != nullptr) { - status = (static_cast<uint32_t>((intptr_t)user_data)) - STATUS_OFFSET; - } else { - if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { - status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ - } - grpc_mdelem_set_user_data( - md, destroy_status, - (void*)static_cast<intptr_t>(status + STATUS_OFFSET)); - } - return status; -} - static grpc_message_compression_algorithm decode_message_compression( grpc_mdelem md) { grpc_message_compression_algorithm algorithm = @@ -1093,7 +1067,8 @@ static void recv_initial_filter(grpc_call* call, grpc_metadata_batch* b) { static void recv_trailing_filter(void* args, grpc_metadata_batch* b) { grpc_call* call = static_cast<grpc_call*>(args); if (b->idx.named.grpc_status != nullptr) { - uint32_t status_code = decode_status(b->idx.named.grpc_status->md); + grpc_status_code status_code = + grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md); grpc_error* error = status_code == GRPC_STATUS_OK ? GRPC_ERROR_NONE @@ -1305,25 +1280,21 @@ static void continue_receiving_slices(batch_control* bctl) { grpc_error* error; grpc_call* call = bctl->call; for (;;) { - size_t remaining = call->receiving_stream->length - + size_t remaining = call->receiving_stream->length() - (*call->receiving_buffer)->data.raw.slice_buffer.length; if (remaining == 0) { call->receiving_message = 0; - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; + call->receiving_stream.reset(); finish_batch_step(bctl); return; } - if (grpc_byte_stream_next(call->receiving_stream, remaining, - &call->receiving_slice_ready)) { - error = - grpc_byte_stream_pull(call->receiving_stream, &call->receiving_slice); + if (call->receiving_stream->Next(remaining, &call->receiving_slice_ready)) { + error = call->receiving_stream->Pull(&call->receiving_slice); if (error == GRPC_ERROR_NONE) { grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, call->receiving_slice); } else { - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; + call->receiving_stream.reset(); grpc_byte_buffer_destroy(*call->receiving_buffer); *call->receiving_buffer = nullptr; call->receiving_message = 0; @@ -1339,19 +1310,17 @@ static void continue_receiving_slices(batch_control* bctl) { static void receiving_slice_ready(void* bctlp, grpc_error* error) { batch_control* bctl = static_cast<batch_control*>(bctlp); grpc_call* call = bctl->call; - grpc_byte_stream* bs = call->receiving_stream; bool release_error = false; if (error == GRPC_ERROR_NONE) { grpc_slice slice; - error = grpc_byte_stream_pull(bs, &slice); + error = call->receiving_stream->Pull(&slice); if (error == GRPC_ERROR_NONE) { grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, slice); continue_receiving_slices(bctl); } else { - /* Error returned by grpc_byte_stream_pull needs to be released manually - */ + /* Error returned by ByteStream::Pull() needs to be released manually */ release_error = true; } } @@ -1360,8 +1329,7 @@ static void receiving_slice_ready(void* bctlp, grpc_error* error) { if (grpc_trace_operation_failures.enabled()) { GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error)); } - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; + call->receiving_stream.reset(); grpc_byte_buffer_destroy(*call->receiving_buffer); *call->receiving_buffer = nullptr; call->receiving_message = 0; @@ -1379,8 +1347,8 @@ static void process_data_after_md(batch_control* bctl) { call->receiving_message = 0; finish_batch_step(bctl); } else { - call->test_only_last_message_flags = call->receiving_stream->flags; - if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) && + call->test_only_last_message_flags = call->receiving_stream->flags(); + if ((call->receiving_stream->flags() & GRPC_WRITE_INTERNAL_COMPRESS) && (call->incoming_message_compression_algorithm > GRPC_MESSAGE_COMPRESS_NONE)) { grpc_compression_algorithm algo; @@ -1403,10 +1371,7 @@ static void receiving_stream_ready(void* bctlp, grpc_error* error) { batch_control* bctl = static_cast<batch_control*>(bctlp); grpc_call* call = bctl->call; if (error != GRPC_ERROR_NONE) { - if (call->receiving_stream != nullptr) { - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; - } + call->receiving_stream.reset(); add_batch_error(bctl, GRPC_ERROR_REF(error), true); cancel_with_error(call, STATUS_FROM_SURFACE, GRPC_ERROR_REF(error)); } @@ -1700,21 +1665,20 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } - stream_op->send_message = true; - call->sending_message = true; - grpc_slice_buffer_stream_init( - &call->sending_stream, - &op->data.send_message.send_message->data.raw.slice_buffer, - op->flags); + uint32_t flags = op->flags; /* If the outgoing buffer is already compressed, mark it as so in the flags. These will be picked up by the compression filter and further (wasteful) attempts at compression skipped. */ if (op->data.send_message.send_message->data.raw.compression > GRPC_COMPRESS_NONE) { - call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; + flags |= GRPC_WRITE_INTERNAL_COMPRESS; } - stream_op_payload->send_message.send_message = - &call->sending_stream.base; + stream_op->send_message = true; + call->sending_message = true; + call->sending_stream.Init( + &op->data.send_message.send_message->data.raw.slice_buffer, flags); + stream_op_payload->send_message.send_message.reset( + call->sending_stream.get()); break; } case GRPC_OP_SEND_CLOSE_FROM_CLIENT: { @@ -1933,7 +1897,7 @@ done_with_error: } if (stream_op->send_message) { call->sending_message = false; - grpc_byte_stream_destroy(&call->sending_stream.base); + call->sending_stream->Orphan(); } if (stream_op->send_trailing_metadata) { call->sent_final_op = false; diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc index 7bc24a5049..ac9f9e6066 100644 --- a/src/core/lib/surface/init.cc +++ b/src/core/lib/surface/init.cc @@ -32,7 +32,7 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/gpr/fork.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/http/parser.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/combiner.h" @@ -123,7 +123,7 @@ void grpc_init(void) { gpr_mu_lock(&g_init_mu); if (++g_initializations == 1) { gpr_time_init(); - gpr_thd_init(); + grpc_core::Thread::Init(); grpc_stats_init(); grpc_slice_intern_init(); grpc_mdctx_global_init(); diff --git a/src/core/lib/surface/lame_client.cc b/src/core/lib/surface/lame_client.cc index f5aca91f97..5a84428b0e 100644 --- a/src/core/lib/surface/lame_client.cc +++ b/src/core/lib/surface/lame_client.cc @@ -52,14 +52,14 @@ struct ChannelData { }; static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) { - CallData* calld = reinterpret_cast<CallData*>(elem->call_data); + CallData* calld = static_cast<CallData*>(elem->call_data); bool expected = false; if (!calld->filled_metadata.compare_exchange_strong( expected, true, grpc_core::memory_order_relaxed, grpc_core::memory_order_relaxed)) { return; } - ChannelData* chand = reinterpret_cast<ChannelData*>(elem->channel_data); + ChannelData* chand = static_cast<ChannelData*>(elem->channel_data); char tmp[GPR_LTOA_MIN_BUFSIZE]; gpr_ltoa(chand->error_code, tmp); calld->status.md = grpc_mdelem_from_slices( @@ -78,7 +78,7 @@ static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) { static void lame_start_transport_stream_op_batch( grpc_call_element* elem, grpc_transport_stream_op_batch* op) { - CallData* calld = reinterpret_cast<CallData*>(elem->call_data); + CallData* calld = static_cast<CallData*>(elem->call_data); if (op->recv_initial_metadata) { fill_metadata(elem, op->payload->recv_initial_metadata.recv_initial_metadata); @@ -119,7 +119,7 @@ static void lame_start_transport_op(grpc_channel_element* elem, static grpc_error* init_call_elem(grpc_call_element* elem, const grpc_call_element_args* args) { - CallData* calld = reinterpret_cast<CallData*>(elem->call_data); + CallData* calld = static_cast<CallData*>(elem->call_data); calld->call_combiner = args->call_combiner; return GRPC_ERROR_NONE; } @@ -172,7 +172,7 @@ grpc_channel* grpc_lame_client_channel_create(const char* target, "error_message=%s)", 3, (target, (int)error_code, error_message)); GPR_ASSERT(elem->filter == &grpc_lame_filter); - auto chand = reinterpret_cast<grpc_core::ChannelData*>(elem->channel_data); + auto chand = static_cast<grpc_core::ChannelData*>(elem->channel_data); chand->error_code = error_code; chand->error_message = error_message; diff --git a/src/core/lib/transport/byte_stream.cc b/src/core/lib/transport/byte_stream.cc index e1751f8010..cb15a71a91 100644 --- a/src/core/lib/transport/byte_stream.cc +++ b/src/core/lib/transport/byte_stream.cc @@ -25,160 +25,136 @@ #include <grpc/support/log.h> +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/slice/slice_internal.h" -bool grpc_byte_stream_next(grpc_byte_stream* byte_stream, size_t max_size_hint, - grpc_closure* on_complete) { - return byte_stream->vtable->next(byte_stream, max_size_hint, on_complete); -} +namespace grpc_core { -grpc_error* grpc_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { - return byte_stream->vtable->pull(byte_stream, slice); -} +// +// SliceBufferByteStream +// -void grpc_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - byte_stream->vtable->shutdown(byte_stream, error); +SliceBufferByteStream::SliceBufferByteStream(grpc_slice_buffer* slice_buffer, + uint32_t flags) + : ByteStream(static_cast<uint32_t>(slice_buffer->length), flags) { + GPR_ASSERT(slice_buffer->length <= UINT32_MAX); + grpc_slice_buffer_init(&backing_buffer_); + grpc_slice_buffer_swap(slice_buffer, &backing_buffer_); } -void grpc_byte_stream_destroy(grpc_byte_stream* byte_stream) { - byte_stream->vtable->destroy(byte_stream); -} +SliceBufferByteStream::~SliceBufferByteStream() {} -// grpc_slice_buffer_stream +void SliceBufferByteStream::Orphan() { + grpc_slice_buffer_destroy(&backing_buffer_); + GRPC_ERROR_UNREF(shutdown_error_); + // Note: We do not actually delete the object here, since + // SliceBufferByteStream is usually allocated as part of a larger + // object and has an OrphanablePtr of itself passed down through the + // filter stack. +} -static bool slice_buffer_stream_next(grpc_byte_stream* byte_stream, - size_t max_size_hint, - grpc_closure* on_complete) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - GPR_ASSERT(stream->cursor < stream->backing_buffer.count); +bool SliceBufferByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + GPR_ASSERT(cursor_ < backing_buffer_.count); return true; } -static grpc_error* slice_buffer_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); +grpc_error* SliceBufferByteStream::Pull(grpc_slice* slice) { + if (shutdown_error_ != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(shutdown_error_); } - GPR_ASSERT(stream->cursor < stream->backing_buffer.count); - *slice = - grpc_slice_ref_internal(stream->backing_buffer.slices[stream->cursor]); - stream->cursor++; + GPR_ASSERT(cursor_ < backing_buffer_.count); + *slice = grpc_slice_ref_internal(backing_buffer_.slices[cursor_]); + ++cursor_; return GRPC_ERROR_NONE; } -static void slice_buffer_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = error; +void SliceBufferByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(shutdown_error_); + shutdown_error_ = error; } -static void slice_buffer_stream_destroy(grpc_byte_stream* byte_stream) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - grpc_slice_buffer_destroy(&stream->backing_buffer); - GRPC_ERROR_UNREF(stream->shutdown_error); +// +// ByteStreamCache +// + +ByteStreamCache::ByteStreamCache(OrphanablePtr<ByteStream> underlying_stream) + : underlying_stream_(std::move(underlying_stream)), + length_(underlying_stream_->length()), + flags_(underlying_stream_->flags()) { + grpc_slice_buffer_init(&cache_buffer_); } -static const grpc_byte_stream_vtable slice_buffer_stream_vtable = { - slice_buffer_stream_next, slice_buffer_stream_pull, - slice_buffer_stream_shutdown, slice_buffer_stream_destroy}; +ByteStreamCache::~ByteStreamCache() { Destroy(); } -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream* stream, - grpc_slice_buffer* slice_buffer, - uint32_t flags) { - GPR_ASSERT(slice_buffer->length <= UINT32_MAX); - stream->base.length = static_cast<uint32_t>(slice_buffer->length); - stream->base.flags = flags; - stream->base.vtable = &slice_buffer_stream_vtable; - grpc_slice_buffer_init(&stream->backing_buffer); - grpc_slice_buffer_swap(slice_buffer, &stream->backing_buffer); - stream->cursor = 0; - stream->shutdown_error = GRPC_ERROR_NONE; +void ByteStreamCache::Destroy() { + underlying_stream_.reset(); + if (cache_buffer_.length > 0) { + grpc_slice_buffer_destroy_internal(&cache_buffer_); + } } -// grpc_caching_byte_stream +// +// ByteStreamCache::CachingByteStream +// -void grpc_byte_stream_cache_init(grpc_byte_stream_cache* cache, - grpc_byte_stream* underlying_stream) { - cache->underlying_stream = underlying_stream; - grpc_slice_buffer_init(&cache->cache_buffer); -} +ByteStreamCache::CachingByteStream::CachingByteStream(ByteStreamCache* cache) + : ByteStream(cache->length_, cache->flags_), cache_(cache) {} -void grpc_byte_stream_cache_destroy(grpc_byte_stream_cache* cache) { - grpc_byte_stream_destroy(cache->underlying_stream); - grpc_slice_buffer_destroy_internal(&cache->cache_buffer); +ByteStreamCache::CachingByteStream::~CachingByteStream() {} + +void ByteStreamCache::CachingByteStream::Orphan() { + GRPC_ERROR_UNREF(shutdown_error_); + // Note: We do not actually delete the object here, since + // CachingByteStream is usually allocated as part of a larger + // object and has an OrphanablePtr of itself passed down through the + // filter stack. } -static bool caching_byte_stream_next(grpc_byte_stream* byte_stream, - size_t max_size_hint, - grpc_closure* on_complete) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - if (stream->shutdown_error != GRPC_ERROR_NONE) return true; - if (stream->cursor < stream->cache->cache_buffer.count) return true; - return grpc_byte_stream_next(stream->cache->underlying_stream, max_size_hint, - on_complete); +bool ByteStreamCache::CachingByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + if (shutdown_error_ != GRPC_ERROR_NONE) return true; + if (cursor_ < cache_->cache_buffer_.count) return true; + GPR_ASSERT(cache_->underlying_stream_ != nullptr); + return cache_->underlying_stream_->Next(max_size_hint, on_complete); } -static grpc_error* caching_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); +grpc_error* ByteStreamCache::CachingByteStream::Pull(grpc_slice* slice) { + if (shutdown_error_ != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(shutdown_error_); } - if (stream->cursor < stream->cache->cache_buffer.count) { - *slice = grpc_slice_ref_internal( - stream->cache->cache_buffer.slices[stream->cursor]); - ++stream->cursor; + if (cursor_ < cache_->cache_buffer_.count) { + *slice = grpc_slice_ref_internal(cache_->cache_buffer_.slices[cursor_]); + ++cursor_; + offset_ += GRPC_SLICE_LENGTH(*slice); return GRPC_ERROR_NONE; } - grpc_error* error = - grpc_byte_stream_pull(stream->cache->underlying_stream, slice); + GPR_ASSERT(cache_->underlying_stream_ != nullptr); + grpc_error* error = cache_->underlying_stream_->Pull(slice); if (error == GRPC_ERROR_NONE) { - ++stream->cursor; - grpc_slice_buffer_add(&stream->cache->cache_buffer, + grpc_slice_buffer_add(&cache_->cache_buffer_, grpc_slice_ref_internal(*slice)); + ++cursor_; + offset_ += GRPC_SLICE_LENGTH(*slice); + // Orphan the underlying stream if it's been drained. + if (offset_ == cache_->underlying_stream_->length()) { + cache_->underlying_stream_.reset(); + } } return error; } -static void caching_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = GRPC_ERROR_REF(error); - grpc_byte_stream_shutdown(stream->cache->underlying_stream, error); -} - -static void caching_byte_stream_destroy(grpc_byte_stream* byte_stream) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - GRPC_ERROR_UNREF(stream->shutdown_error); +void ByteStreamCache::CachingByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(shutdown_error_); + shutdown_error_ = GRPC_ERROR_REF(error); + if (cache_->underlying_stream_ != nullptr) { + cache_->underlying_stream_->Shutdown(error); + } } -static const grpc_byte_stream_vtable caching_byte_stream_vtable = { - caching_byte_stream_next, caching_byte_stream_pull, - caching_byte_stream_shutdown, caching_byte_stream_destroy}; - -void grpc_caching_byte_stream_init(grpc_caching_byte_stream* stream, - grpc_byte_stream_cache* cache) { - memset(stream, 0, sizeof(*stream)); - stream->base.length = cache->underlying_stream->length; - stream->base.flags = cache->underlying_stream->flags; - stream->base.vtable = &caching_byte_stream_vtable; - stream->cache = cache; - stream->shutdown_error = GRPC_ERROR_NONE; +void ByteStreamCache::CachingByteStream::Reset() { + cursor_ = 0; + offset_ = 0; } -void grpc_caching_byte_stream_reset(grpc_caching_byte_stream* stream) { - stream->cursor = 0; -} +} // namespace grpc_core diff --git a/src/core/lib/transport/byte_stream.h b/src/core/lib/transport/byte_stream.h index 4d3c3c131b..95a756a5e4 100644 --- a/src/core/lib/transport/byte_stream.h +++ b/src/core/lib/transport/byte_stream.h @@ -22,6 +22,8 @@ #include <grpc/support/port_platform.h> #include <grpc/slice_buffer.h> +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/orphanable.h" #include "src/core/lib/iomgr/exec_ctx.h" /** Internal bit flag for grpc_begin_message's \a flags signaling the use of @@ -30,71 +32,82 @@ /** Mask of all valid internal flags. */ #define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS) -typedef struct grpc_byte_stream grpc_byte_stream; - -typedef struct { - bool (*next)(grpc_byte_stream* byte_stream, size_t max_size_hint, - grpc_closure* on_complete); - grpc_error* (*pull)(grpc_byte_stream* byte_stream, grpc_slice* slice); - void (*shutdown)(grpc_byte_stream* byte_stream, grpc_error* error); - void (*destroy)(grpc_byte_stream* byte_stream); -} grpc_byte_stream_vtable; - -struct grpc_byte_stream { - uint32_t length; - uint32_t flags; - const grpc_byte_stream_vtable* vtable; +namespace grpc_core { + +class ByteStream : public Orphanable { + public: + virtual ~ByteStream() {} + + // Returns true if the bytes are available immediately (in which case + // on_complete will not be called), or false if the bytes will be available + // asynchronously (in which case on_complete will be called when they + // are available). + // + // max_size_hint can be set as a hint as to the maximum number + // of bytes that would be acceptable to read. + virtual bool Next(size_t max_size_hint, + grpc_closure* on_complete) GRPC_ABSTRACT; + + // Returns the next slice in the byte stream when it is available, as + // indicated by Next(). + // + // Once a slice is returned into *slice, it is owned by the caller. + virtual grpc_error* Pull(grpc_slice* slice) GRPC_ABSTRACT; + + // Shuts down the byte stream. + // + // If there is a pending call to on_complete from Next(), it will be + // invoked with the error passed to Shutdown(). + // + // The next call to Pull() (if any) will return the error passed to + // Shutdown(). + virtual void Shutdown(grpc_error* error) GRPC_ABSTRACT; + + uint32_t length() const { return length_; } + uint32_t flags() const { return flags_; } + + void set_flags(uint32_t flags) { flags_ = flags; } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + ByteStream(uint32_t length, uint32_t flags) + : length_(length), flags_(flags) {} + + private: + const uint32_t length_; + uint32_t flags_; }; -// Returns true if the bytes are available immediately (in which case -// on_complete will not be called), false if the bytes will be available -// asynchronously. // -// max_size_hint can be set as a hint as to the maximum number -// of bytes that would be acceptable to read. -bool grpc_byte_stream_next(grpc_byte_stream* byte_stream, size_t max_size_hint, - grpc_closure* on_complete); - -// Returns the next slice in the byte stream when it is ready (indicated by -// either grpc_byte_stream_next returning true or on_complete passed to -// grpc_byte_stream_next is called). +// SliceBufferByteStream // -// Once a slice is returned into *slice, it is owned by the caller. -grpc_error* grpc_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice); - -// Shuts down the byte stream. +// A ByteStream that wraps a slice buffer. // -// If there is a pending call to on_complete from grpc_byte_stream_next(), -// it will be invoked with the error passed to grpc_byte_stream_shutdown(). -// -// The next call to grpc_byte_stream_pull() (if any) will return the error -// passed to grpc_byte_stream_shutdown(). -void grpc_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error); -void grpc_byte_stream_destroy(grpc_byte_stream* byte_stream); +class SliceBufferByteStream : public ByteStream { + public: + // Removes all slices in slice_buffer, leaving it empty. + SliceBufferByteStream(grpc_slice_buffer* slice_buffer, uint32_t flags); + + ~SliceBufferByteStream(); + + void Orphan() override; + + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; + + private: + grpc_slice_buffer backing_buffer_; + size_t cursor_ = 0; + grpc_error* shutdown_error_ = GRPC_ERROR_NONE; +}; -// grpc_slice_buffer_stream // -// A grpc_byte_stream that wraps a slice buffer. The stream takes -// ownership of the slices in the buffer, and on destruction will -// reset the contents of the buffer. - -typedef struct grpc_slice_buffer_stream { - grpc_byte_stream base; - grpc_slice_buffer backing_buffer; - size_t cursor; - grpc_error* shutdown_error; -} grpc_slice_buffer_stream; - -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream* stream, - grpc_slice_buffer* slice_buffer, - uint32_t flags); - -// grpc_caching_byte_stream +// CachingByteStream // -// A grpc_byte_stream that that wraps an underlying byte stream but caches +// A ByteStream that that wraps an underlying byte stream but caches // the resulting slices in a slice buffer. If an initial attempt fails // without fully draining the underlying stream, a new caching stream // can be created from the same underlying cache, in which case it will @@ -102,32 +115,50 @@ void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream* stream, // underlying stream. // // NOTE: No synchronization is done, so it is not safe to have multiple -// grpc_caching_byte_streams simultaneously drawing from the same underlying -// grpc_byte_stream_cache at the same time. +// CachingByteStreams simultaneously drawing from the same underlying +// ByteStreamCache at the same time. +// + +class ByteStreamCache { + public: + class CachingByteStream : public ByteStream { + public: + explicit CachingByteStream(ByteStreamCache* cache); + + ~CachingByteStream(); + + void Orphan() override; -typedef struct { - grpc_byte_stream* underlying_stream; - grpc_slice_buffer cache_buffer; -} grpc_byte_stream_cache; + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; -// Takes ownership of underlying_stream. -void grpc_byte_stream_cache_init(grpc_byte_stream_cache* cache, - grpc_byte_stream* underlying_stream); + // Resets the byte stream to the start of the underlying stream. + void Reset(); -// Must not be called while still in use by a grpc_caching_byte_stream. -void grpc_byte_stream_cache_destroy(grpc_byte_stream_cache* cache); + private: + ByteStreamCache* cache_; + size_t cursor_ = 0; + size_t offset_ = 0; + grpc_error* shutdown_error_ = GRPC_ERROR_NONE; + }; -typedef struct { - grpc_byte_stream base; - grpc_byte_stream_cache* cache; - size_t cursor; - grpc_error* shutdown_error; -} grpc_caching_byte_stream; + explicit ByteStreamCache(OrphanablePtr<ByteStream> underlying_stream); -void grpc_caching_byte_stream_init(grpc_caching_byte_stream* stream, - grpc_byte_stream_cache* cache); + ~ByteStreamCache(); + + // Must not be destroyed while still in use by a CachingByteStream. + void Destroy(); + + grpc_slice_buffer* cache_buffer() { return &cache_buffer_; } + + private: + OrphanablePtr<ByteStream> underlying_stream_; + uint32_t length_; + uint32_t flags_; + grpc_slice_buffer cache_buffer_; +}; -// Resets the byte stream to the start of the underlying stream. -void grpc_caching_byte_stream_reset(grpc_caching_byte_stream* stream); +} // namespace grpc_core #endif /* GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H */ diff --git a/src/core/lib/transport/metadata_batch.cc b/src/core/lib/transport/metadata_batch.cc index b23f516516..49740fcd1e 100644 --- a/src/core/lib/transport/metadata_batch.cc +++ b/src/core/lib/transport/metadata_batch.cc @@ -303,3 +303,27 @@ grpc_error* grpc_metadata_batch_filter(grpc_metadata_batch* batch, } return error; } + +void grpc_metadata_batch_copy(grpc_metadata_batch* src, + grpc_metadata_batch* dst, + grpc_linked_mdelem* storage) { + grpc_metadata_batch_init(dst); + dst->deadline = src->deadline; + size_t i = 0; + for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr; + elem = elem->next) { + grpc_error* error = grpc_metadata_batch_add_tail(dst, &storage[i++], + GRPC_MDELEM_REF(elem->md)); + // The only way that grpc_metadata_batch_add_tail() can fail is if + // there's a duplicate entry for a callout. However, that can't be + // the case here, because we would not have been allowed to create + // a source batch that had that kind of conflict. + GPR_ASSERT(error == GRPC_ERROR_NONE); + } +} + +void grpc_metadata_batch_move(grpc_metadata_batch* src, + grpc_metadata_batch* dst) { + *dst = *src; + grpc_metadata_batch_init(src); +} diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h index 06fc9ade7e..3876063b52 100644 --- a/src/core/lib/transport/metadata_batch.h +++ b/src/core/lib/transport/metadata_batch.h @@ -137,4 +137,13 @@ void grpc_metadata_batch_assert_ok(grpc_metadata_batch* comd); } while (0) #endif +/// Copies \a src to \a dst. \a storage must point to an array of +/// \a grpc_linked_mdelem structs of at least the same size as \a src. +void grpc_metadata_batch_copy(grpc_metadata_batch* src, + grpc_metadata_batch* dst, + grpc_linked_mdelem* storage); + +void grpc_metadata_batch_move(grpc_metadata_batch* src, + grpc_metadata_batch* dst); + #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */ diff --git a/src/core/lib/transport/service_config.cc b/src/core/lib/transport/service_config.cc index b1d727419d..e1a55d98ab 100644 --- a/src/core/lib/transport/service_config.cc +++ b/src/core/lib/transport/service_config.cc @@ -31,74 +31,30 @@ #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" -// The main purpose of the code here is to parse the service config in -// JSON form, which will look like this: -// -// { -// "loadBalancingPolicy": "string", // optional -// "methodConfig": [ // array of one or more method_config objects -// { -// "name": [ // array of one or more name objects -// { -// "service": "string", // required -// "method": "string", // optional -// } -// ], -// // remaining fields are optional. -// // see https://developers.google.com/protocol-buffers/docs/proto3#json -// // for format details. -// "waitForReady": bool, -// "timeout": "duration_string", -// "maxRequestMessageBytes": "int64_string", -// "maxResponseMessageBytes": "int64_string", -// } -// ] -// } - -struct grpc_service_config { - char* json_string; // Underlying storage for json_tree. - grpc_json* json_tree; -}; +namespace grpc_core { -grpc_service_config* grpc_service_config_create(const char* json_string) { - grpc_service_config* service_config = - static_cast<grpc_service_config*>(gpr_malloc(sizeof(*service_config))); - service_config->json_string = gpr_strdup(json_string); - service_config->json_tree = - grpc_json_parse_string(service_config->json_string); - if (service_config->json_tree == nullptr) { +UniquePtr<ServiceConfig> ServiceConfig::Create(const char* json) { + UniquePtr<char> json_string(gpr_strdup(json)); + grpc_json* json_tree = grpc_json_parse_string(json_string.get()); + if (json_tree == nullptr) { gpr_log(GPR_INFO, "failed to parse JSON for service config"); - gpr_free(service_config->json_string); - gpr_free(service_config); return nullptr; } - return service_config; + return MakeUnique<ServiceConfig>(std::move(json_string), json_tree); } -void grpc_service_config_destroy(grpc_service_config* service_config) { - grpc_json_destroy(service_config->json_tree); - gpr_free(service_config->json_string); - gpr_free(service_config); -} +ServiceConfig::ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree) + : json_string_(std::move(json_string)), json_tree_(json_tree) {} -void grpc_service_config_parse_global_params( - const grpc_service_config* service_config, - void (*process_json)(const grpc_json* json, void* arg), void* arg) { - const grpc_json* json = service_config->json_tree; - if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { - if (field->key == nullptr) return; - if (strcmp(field->key, "methodConfig") == 0) continue; - process_json(field, arg); - } -} +ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); } -const char* grpc_service_config_get_lb_policy_name( - const grpc_service_config* service_config) { - const grpc_json* json = service_config->json_tree; - if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return nullptr; +const char* ServiceConfig::GetLoadBalancingPolicyName() const { + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return nullptr; + } const char* lb_policy_name = nullptr; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { if (field->key == nullptr) return nullptr; if (strcmp(field->key, "loadBalancingPolicy") == 0) { if (lb_policy_name != nullptr) return nullptr; // Duplicate. @@ -109,8 +65,7 @@ const char* grpc_service_config_get_lb_policy_name( return lb_policy_name; } -// Returns the number of names specified in the method config \a json. -static size_t count_names_in_method_config_json(grpc_json* json) { +size_t ServiceConfig::CountNamesInMethodConfig(grpc_json* json) { size_t num_names = 0; for (grpc_json* field = json->child; field != nullptr; field = field->next) { if (field->key != nullptr && strcmp(field->key, "name") == 0) { @@ -124,9 +79,7 @@ static size_t count_names_in_method_config_json(grpc_json* json) { return num_names; } -// Returns a path string for the JSON name object specified by \a json. -// Returns NULL on error. Caller takes ownership of result. -static char* parse_json_method_name(grpc_json* json) { +UniquePtr<char> ServiceConfig::ParseJsonMethodName(grpc_json* json) { if (json->type != GRPC_JSON_OBJECT) return nullptr; const char* service_name = nullptr; const char* method_name = nullptr; @@ -147,116 +100,7 @@ static char* parse_json_method_name(grpc_json* json) { char* path; gpr_asprintf(&path, "/%s/%s", service_name, method_name == nullptr ? "*" : method_name); - return path; + return UniquePtr<char>(path); } -// Parses the method config from \a json. Adds an entry to \a entries for -// each name found, incrementing \a idx for each entry added. -// Returns false on error. -static bool parse_json_method_config( - grpc_json* json, void* (*create_value)(const grpc_json* method_config_json), - void* (*ref_value)(void* value), void (*unref_value)(void* value), - grpc_slice_hash_table_entry* entries, size_t* idx) { - // Construct value. - void* method_config = create_value(json); - if (method_config == nullptr) return false; - // Construct list of paths. - bool success = false; - gpr_strvec paths; - gpr_strvec_init(&paths); - for (grpc_json* child = json->child; child != nullptr; child = child->next) { - if (child->key == nullptr) continue; - if (strcmp(child->key, "name") == 0) { - if (child->type != GRPC_JSON_ARRAY) goto done; - for (grpc_json* name = child->child; name != nullptr; name = name->next) { - char* path = parse_json_method_name(name); - if (path == nullptr) goto done; - gpr_strvec_add(&paths, path); - } - } - } - if (paths.count == 0) goto done; // No names specified. - // Add entry for each path. - for (size_t i = 0; i < paths.count; ++i) { - entries[*idx].key = grpc_slice_from_copied_string(paths.strs[i]); - entries[*idx].value = ref_value(method_config); - ++*idx; - } - success = true; -done: - unref_value(method_config); - gpr_strvec_destroy(&paths); - return success; -} - -grpc_slice_hash_table* grpc_service_config_create_method_config_table( - const grpc_service_config* service_config, - void* (*create_value)(const grpc_json* method_config_json), - void* (*ref_value)(void* value), void (*unref_value)(void* value)) { - const grpc_json* json = service_config->json_tree; - // Traverse parsed JSON tree. - if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return nullptr; - size_t num_entries = 0; - grpc_slice_hash_table_entry* entries = nullptr; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { - if (field->key == nullptr) return nullptr; - if (strcmp(field->key, "methodConfig") == 0) { - if (entries != nullptr) return nullptr; // Duplicate. - if (field->type != GRPC_JSON_ARRAY) return nullptr; - // Find number of entries. - for (grpc_json* method = field->child; method != nullptr; - method = method->next) { - size_t count = count_names_in_method_config_json(method); - if (count <= 0) return nullptr; - num_entries += count; - } - // Populate method config table entries. - entries = static_cast<grpc_slice_hash_table_entry*>( - gpr_malloc(num_entries * sizeof(grpc_slice_hash_table_entry))); - size_t idx = 0; - for (grpc_json* method = field->child; method != nullptr; - method = method->next) { - if (!parse_json_method_config(method, create_value, ref_value, - unref_value, entries, &idx)) { - for (size_t i = 0; i < idx; ++i) { - grpc_slice_unref_internal(entries[i].key); - unref_value(entries[i].value); - } - gpr_free(entries); - return nullptr; - } - } - GPR_ASSERT(idx == num_entries); - } - } - // Instantiate method config table. - grpc_slice_hash_table* method_config_table = nullptr; - if (entries != nullptr) { - method_config_table = grpc_slice_hash_table_create(num_entries, entries, - unref_value, nullptr); - gpr_free(entries); - } - return method_config_table; -} - -void* grpc_method_config_table_get(const grpc_slice_hash_table* table, - grpc_slice path) { - void* value = grpc_slice_hash_table_get(table, path); - // If we didn't find a match for the path, try looking for a wildcard - // entry (i.e., change "/service/method" to "/service/*"). - if (value == nullptr) { - char* path_str = grpc_slice_to_c_string(path); - const char* sep = strrchr(path_str, '/') + 1; - const size_t len = static_cast<size_t>(sep - path_str); - char* buf = static_cast<char*>(gpr_malloc(len + 2)); // '*' and NUL - memcpy(buf, path_str, len); - buf[len] = '*'; - buf[len + 1] = '\0'; - grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); - gpr_free(buf); - value = grpc_slice_hash_table_get(table, wildcard_path); - grpc_slice_unref_internal(wildcard_path); - gpr_free(path_str); - } - return value; -} +} // namespace grpc_core diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h index 6517f36802..a65b267d46 100644 --- a/src/core/lib/transport/service_config.h +++ b/src/core/lib/transport/service_config.h @@ -20,44 +20,230 @@ #include <grpc/support/port_platform.h> #include <grpc/impl/codegen/grpc_types.h> +#include <grpc/support/string_util.h> +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/json/json.h" #include "src/core/lib/slice/slice_hash_table.h" -typedef struct grpc_service_config grpc_service_config; - -grpc_service_config* grpc_service_config_create(const char* json_string); -void grpc_service_config_destroy(grpc_service_config* service_config); - -/// Invokes \a process_json() for each global parameter in the service -/// config. \a arg is passed as the second argument to \a process_json(). -void grpc_service_config_parse_global_params( - const grpc_service_config* service_config, - void (*process_json)(const grpc_json* json, void* arg), void* arg); - -/// Gets the LB policy name from \a service_config. -/// Returns NULL if no LB policy name was specified. -/// Caller does NOT take ownership. -const char* grpc_service_config_get_lb_policy_name( - const grpc_service_config* service_config); - -/// Creates a method config table based on the data in \a json. -/// The table's keys are request paths. The table's value type is -/// returned by \a create_value(), based on data parsed from the JSON tree. -/// \a ref_value() and \a unref_value() are used to ref and unref values. -/// Returns NULL on error. -grpc_slice_hash_table* grpc_service_config_create_method_config_table( - const grpc_service_config* service_config, - void* (*create_value)(const grpc_json* method_config_json), - void* (*ref_value)(void* value), void (*unref_value)(void* value)); - -/// A helper function for looking up values in the table returned by -/// \a grpc_service_config_create_method_config_table(). -/// Gets the method config for the specified \a path, which should be of -/// the form "/service/method". -/// Returns NULL if the method has no config. -/// Caller does NOT own a reference to the result. -void* grpc_method_config_table_get(const grpc_slice_hash_table* table, - grpc_slice path); +// The main purpose of the code here is to parse the service config in +// JSON form, which will look like this: +// +// { +// "loadBalancingPolicy": "string", // optional +// "methodConfig": [ // array of one or more method_config objects +// { +// "name": [ // array of one or more name objects +// { +// "service": "string", // required +// "method": "string", // optional +// } +// ], +// // remaining fields are optional. +// // see +// https://developers.google.com/protocol-buffers/docs/proto3#json +// // for format details. +// "waitForReady": bool, +// "timeout": "duration_string", +// "maxRequestMessageBytes": "int64_string", +// "maxResponseMessageBytes": "int64_string", +// } +// ] +// } + +namespace grpc_core { + +class ServiceConfig { + public: + /// Creates a new service config from parsing \a json_string. + /// Returns null on parse error. + static UniquePtr<ServiceConfig> Create(const char* json); + + ~ServiceConfig(); + + /// Invokes \a process_json() for each global parameter in the service + /// config. \a arg is passed as the second argument to \a process_json(). + template <typename T> + using ProcessJson = void (*)(const grpc_json*, T*); + template <typename T> + void ParseGlobalParams(ProcessJson<T> process_json, T* arg) const; + + /// Gets the LB policy name from \a service_config. + /// Returns NULL if no LB policy name was specified. + /// Caller does NOT take ownership. + const char* GetLoadBalancingPolicyName() const; + + /// Creates a method config table based on the data in \a json. + /// The table's keys are request paths. The table's value type is + /// returned by \a create_value(), based on data parsed from the JSON tree. + /// Returns null on error. + template <typename T> + using CreateValue = RefCountedPtr<T> (*)(const grpc_json* method_config_json); + template <typename T> + RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> CreateMethodConfigTable( + CreateValue<T> create_value); + + /// A helper function for looking up values in the table returned by + /// \a CreateMethodConfigTable(). + /// Gets the method config for the specified \a path, which should be of + /// the form "/service/method". + /// Returns null if the method has no config. + /// Caller does NOT own a reference to the result. + template <typename T> + static RefCountedPtr<T> MethodConfigTableLookup( + const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path); + + private: + // So New() can call our private ctor. + template <typename T, typename... Args> + friend T* New(Args&&... args); + + // Takes ownership of \a json_tree. + ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree); + + // Returns the number of names specified in the method config \a json. + static size_t CountNamesInMethodConfig(grpc_json* json); + + // Returns a path string for the JSON name object specified by \a json. + // Returns null on error. + static UniquePtr<char> ParseJsonMethodName(grpc_json* json); + + // Parses the method config from \a json. Adds an entry to \a entries for + // each name found, incrementing \a idx for each entry added. + // Returns false on error. + template <typename T> + static bool ParseJsonMethodConfig( + grpc_json* json, CreateValue<T> create_value, + typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx); + + UniquePtr<char> json_string_; // Underlying storage for json_tree. + grpc_json* json_tree_; +}; + +// +// implementation -- no user-serviceable parts below +// + +template <typename T> +void ServiceConfig::ParseGlobalParams(ProcessJson<T> process_json, + T* arg) const { + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return; + } + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return; + if (strcmp(field->key, "methodConfig") == 0) continue; + process_json(field, arg); + } +} + +template <typename T> +bool ServiceConfig::ParseJsonMethodConfig( + grpc_json* json, CreateValue<T> create_value, + typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx) { + // Construct value. + RefCountedPtr<T> method_config = create_value(json); + if (method_config == nullptr) return false; + // Construct list of paths. + InlinedVector<UniquePtr<char>, 10> paths; + for (grpc_json* child = json->child; child != nullptr; child = child->next) { + if (child->key == nullptr) continue; + if (strcmp(child->key, "name") == 0) { + if (child->type != GRPC_JSON_ARRAY) return false; + for (grpc_json* name = child->child; name != nullptr; name = name->next) { + UniquePtr<char> path = ParseJsonMethodName(name); + if (path == nullptr) return false; + paths.push_back(std::move(path)); + } + } + } + if (paths.size() == 0) return false; // No names specified. + // Add entry for each path. + for (size_t i = 0; i < paths.size(); ++i) { + entries[*idx].key = grpc_slice_from_copied_string(paths[i].get()); + entries[*idx].value = method_config; // Takes a new ref. + ++*idx; + } + // Success. + return true; +} + +template <typename T> +RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> +ServiceConfig::CreateMethodConfigTable(CreateValue<T> create_value) { + // Traverse parsed JSON tree. + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return nullptr; + } + size_t num_entries = 0; + typename SliceHashTable<RefCountedPtr<T>>::Entry* entries = nullptr; + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return nullptr; + if (strcmp(field->key, "methodConfig") == 0) { + if (entries != nullptr) return nullptr; // Duplicate. + if (field->type != GRPC_JSON_ARRAY) return nullptr; + // Find number of entries. + for (grpc_json* method = field->child; method != nullptr; + method = method->next) { + size_t count = CountNamesInMethodConfig(method); + if (count <= 0) return nullptr; + num_entries += count; + } + // Populate method config table entries. + entries = static_cast<typename SliceHashTable<RefCountedPtr<T>>::Entry*>( + gpr_zalloc(num_entries * + sizeof(typename SliceHashTable<RefCountedPtr<T>>::Entry))); + size_t idx = 0; + for (grpc_json* method = field->child; method != nullptr; + method = method->next) { + if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) { + for (size_t i = 0; i < idx; ++i) { + grpc_slice_unref_internal(entries[i].key); + entries[i].value.reset(); + } + gpr_free(entries); + return nullptr; + } + } + GPR_ASSERT(idx == num_entries); + } + } + // Instantiate method config table. + RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> method_config_table; + if (entries != nullptr) { + method_config_table = + SliceHashTable<RefCountedPtr<T>>::Create(num_entries, entries, nullptr); + gpr_free(entries); + } + return method_config_table; +} + +template <typename T> +RefCountedPtr<T> ServiceConfig::MethodConfigTableLookup( + const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path) { + const RefCountedPtr<T>* value = table.Get(path); + // If we didn't find a match for the path, try looking for a wildcard + // entry (i.e., change "/service/method" to "/service/*"). + if (value == nullptr) { + char* path_str = grpc_slice_to_c_string(path); + const char* sep = strrchr(path_str, '/') + 1; + const size_t len = (size_t)(sep - path_str); + char* buf = (char*)gpr_malloc(len + 2); // '*' and NUL + memcpy(buf, path_str, len); + buf[len] = '*'; + buf[len + 1] = '\0'; + grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); + gpr_free(buf); + value = table.Get(wildcard_path); + grpc_slice_unref_internal(wildcard_path); + gpr_free(path_str); + } + return RefCountedPtr<T>(*value); +} + +} // namespace grpc_core #endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */ diff --git a/src/core/lib/transport/static_metadata.cc b/src/core/lib/transport/static_metadata.cc index 0e11b6e4e4..6a5144f21a 100644 --- a/src/core/lib/transport/static_metadata.cc +++ b/src/core/lib/transport/static_metadata.cc @@ -50,61 +50,64 @@ static uint8_t g_bytes[] = { 114, 110, 97, 108, 45, 115, 116, 114, 101, 97, 109, 45, 101, 110, 99, 111, 100, 105, 110, 103, 45, 114, 101, 113, 117, 101, 115, 116, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 104, 111, 115, 116, 108, 98, 45, - 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 116, 105, 109, 101, 111, - 117, 116, 103, 114, 112, 99, 46, 119, 97, 105, 116, 95, 102, 111, 114, - 95, 114, 101, 97, 100, 121, 103, 114, 112, 99, 46, 116, 105, 109, 101, - 111, 117, 116, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 113, - 117, 101, 115, 116, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, 121, - 116, 101, 115, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 115, - 112, 111, 110, 115, 101, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, - 121, 116, 101, 115, 47, 103, 114, 112, 99, 46, 108, 98, 46, 118, 49, - 46, 76, 111, 97, 100, 66, 97, 108, 97, 110, 99, 101, 114, 47, 66, - 97, 108, 97, 110, 99, 101, 76, 111, 97, 100, 100, 101, 102, 108, 97, - 116, 101, 103, 122, 105, 112, 115, 116, 114, 101, 97, 109, 47, 103, 122, - 105, 112, 48, 49, 50, 105, 100, 101, 110, 116, 105, 116, 121, 116, 114, - 97, 105, 108, 101, 114, 115, 97, 112, 112, 108, 105, 99, 97, 116, 105, - 111, 110, 47, 103, 114, 112, 99, 80, 79, 83, 84, 50, 48, 48, 52, - 48, 52, 104, 116, 116, 112, 104, 116, 116, 112, 115, 103, 114, 112, 99, - 71, 69, 84, 80, 85, 84, 47, 47, 105, 110, 100, 101, 120, 46, 104, - 116, 109, 108, 50, 48, 52, 50, 48, 54, 51, 48, 52, 52, 48, 48, - 53, 48, 48, 97, 99, 99, 101, 112, 116, 45, 99, 104, 97, 114, 115, - 101, 116, 103, 122, 105, 112, 44, 32, 100, 101, 102, 108, 97, 116, 101, - 97, 99, 99, 101, 112, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, - 97, 99, 99, 101, 112, 116, 45, 114, 97, 110, 103, 101, 115, 97, 99, - 99, 101, 112, 116, 97, 99, 99, 101, 115, 115, 45, 99, 111, 110, 116, - 114, 111, 108, 45, 97, 108, 108, 111, 119, 45, 111, 114, 105, 103, 105, - 110, 97, 103, 101, 97, 108, 108, 111, 119, 97, 117, 116, 104, 111, 114, - 105, 122, 97, 116, 105, 111, 110, 99, 97, 99, 104, 101, 45, 99, 111, - 110, 116, 114, 111, 108, 99, 111, 110, 116, 101, 110, 116, 45, 100, 105, - 115, 112, 111, 115, 105, 116, 105, 111, 110, 99, 111, 110, 116, 101, 110, - 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, 99, 111, 110, 116, 101, - 110, 116, 45, 108, 101, 110, 103, 116, 104, 99, 111, 110, 116, 101, 110, - 116, 45, 108, 111, 99, 97, 116, 105, 111, 110, 99, 111, 110, 116, 101, - 110, 116, 45, 114, 97, 110, 103, 101, 99, 111, 111, 107, 105, 101, 100, - 97, 116, 101, 101, 116, 97, 103, 101, 120, 112, 101, 99, 116, 101, 120, - 112, 105, 114, 101, 115, 102, 114, 111, 109, 105, 102, 45, 109, 97, 116, - 99, 104, 105, 102, 45, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, - 105, 110, 99, 101, 105, 102, 45, 110, 111, 110, 101, 45, 109, 97, 116, - 99, 104, 105, 102, 45, 114, 97, 110, 103, 101, 105, 102, 45, 117, 110, - 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, 105, 110, 99, 101, 108, - 97, 115, 116, 45, 109, 111, 100, 105, 102, 105, 101, 100, 108, 98, 45, - 99, 111, 115, 116, 45, 98, 105, 110, 108, 105, 110, 107, 108, 111, 99, - 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, 111, 114, 119, 97, 114, - 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, 116, 104, 101, 110, 116, - 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, 45, 97, 117, 116, 104, - 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, 97, 110, 103, 101, 114, - 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, 115, 104, 114, 101, - 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, 101, 114, 118, 101, 114, - 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, 115, 116, 114, 105, 99, - 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, 116, 45, 115, 101, 99, - 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, 102, 101, 114, 45, 101, - 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, 121, 118, 105, 97, 119, - 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 101, - 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, 116, - 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 103, 122, 105, 112, 100, - 101, 102, 108, 97, 116, 101, 44, 103, 122, 105, 112, 105, 100, 101, 110, - 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, 116, 101, 44, 103, 122, - 105, 112}; + 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 112, 114, 101, 118, 105, + 111, 117, 115, 45, 114, 112, 99, 45, 97, 116, 116, 101, 109, 112, 116, + 115, 103, 114, 112, 99, 45, 114, 101, 116, 114, 121, 45, 112, 117, 115, + 104, 98, 97, 99, 107, 45, 109, 115, 103, 114, 112, 99, 45, 116, 105, + 109, 101, 111, 117, 116, 49, 50, 51, 52, 103, 114, 112, 99, 46, 119, + 97, 105, 116, 95, 102, 111, 114, 95, 114, 101, 97, 100, 121, 103, 114, + 112, 99, 46, 116, 105, 109, 101, 111, 117, 116, 103, 114, 112, 99, 46, + 109, 97, 120, 95, 114, 101, 113, 117, 101, 115, 116, 95, 109, 101, 115, + 115, 97, 103, 101, 95, 98, 121, 116, 101, 115, 103, 114, 112, 99, 46, + 109, 97, 120, 95, 114, 101, 115, 112, 111, 110, 115, 101, 95, 109, 101, + 115, 115, 97, 103, 101, 95, 98, 121, 116, 101, 115, 47, 103, 114, 112, + 99, 46, 108, 98, 46, 118, 49, 46, 76, 111, 97, 100, 66, 97, 108, + 97, 110, 99, 101, 114, 47, 66, 97, 108, 97, 110, 99, 101, 76, 111, + 97, 100, 100, 101, 102, 108, 97, 116, 101, 103, 122, 105, 112, 115, 116, + 114, 101, 97, 109, 47, 103, 122, 105, 112, 48, 105, 100, 101, 110, 116, + 105, 116, 121, 116, 114, 97, 105, 108, 101, 114, 115, 97, 112, 112, 108, + 105, 99, 97, 116, 105, 111, 110, 47, 103, 114, 112, 99, 80, 79, 83, + 84, 50, 48, 48, 52, 48, 52, 104, 116, 116, 112, 104, 116, 116, 112, + 115, 103, 114, 112, 99, 71, 69, 84, 80, 85, 84, 47, 47, 105, 110, + 100, 101, 120, 46, 104, 116, 109, 108, 50, 48, 52, 50, 48, 54, 51, + 48, 52, 52, 48, 48, 53, 48, 48, 97, 99, 99, 101, 112, 116, 45, + 99, 104, 97, 114, 115, 101, 116, 103, 122, 105, 112, 44, 32, 100, 101, + 102, 108, 97, 116, 101, 97, 99, 99, 101, 112, 116, 45, 108, 97, 110, + 103, 117, 97, 103, 101, 97, 99, 99, 101, 112, 116, 45, 114, 97, 110, + 103, 101, 115, 97, 99, 99, 101, 112, 116, 97, 99, 99, 101, 115, 115, + 45, 99, 111, 110, 116, 114, 111, 108, 45, 97, 108, 108, 111, 119, 45, + 111, 114, 105, 103, 105, 110, 97, 103, 101, 97, 108, 108, 111, 119, 97, + 117, 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 99, 97, 99, + 104, 101, 45, 99, 111, 110, 116, 114, 111, 108, 99, 111, 110, 116, 101, + 110, 116, 45, 100, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 99, + 111, 110, 116, 101, 110, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, + 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 99, + 111, 110, 116, 101, 110, 116, 45, 108, 111, 99, 97, 116, 105, 111, 110, + 99, 111, 110, 116, 101, 110, 116, 45, 114, 97, 110, 103, 101, 99, 111, + 111, 107, 105, 101, 100, 97, 116, 101, 101, 116, 97, 103, 101, 120, 112, + 101, 99, 116, 101, 120, 112, 105, 114, 101, 115, 102, 114, 111, 109, 105, + 102, 45, 109, 97, 116, 99, 104, 105, 102, 45, 109, 111, 100, 105, 102, + 105, 101, 100, 45, 115, 105, 110, 99, 101, 105, 102, 45, 110, 111, 110, + 101, 45, 109, 97, 116, 99, 104, 105, 102, 45, 114, 97, 110, 103, 101, + 105, 102, 45, 117, 110, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, + 105, 110, 99, 101, 108, 97, 115, 116, 45, 109, 111, 100, 105, 102, 105, + 101, 100, 108, 98, 45, 99, 111, 115, 116, 45, 98, 105, 110, 108, 105, + 110, 107, 108, 111, 99, 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, + 111, 114, 119, 97, 114, 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, + 116, 104, 101, 110, 116, 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, + 45, 97, 117, 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, + 97, 110, 103, 101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, + 101, 115, 104, 114, 101, 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, + 101, 114, 118, 101, 114, 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, + 115, 116, 114, 105, 99, 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, + 116, 45, 115, 101, 99, 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, + 102, 101, 114, 45, 101, 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, + 121, 118, 105, 97, 119, 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, + 105, 99, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, + 101, 102, 108, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, + 103, 122, 105, 112, 100, 101, 102, 108, 97, 116, 101, 44, 103, 122, 105, + 112, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, + 116, 101, 44, 103, 122, 105, 112}; static void static_ref(void* unused) {} static void static_unref(void* unused) {} @@ -217,6 +220,10 @@ grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = { {&grpc_static_metadata_vtable, &static_sub_refcnt}, {&grpc_static_metadata_vtable, &static_sub_refcnt}, {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, }; const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { @@ -242,85 +249,89 @@ const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, - {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}, - {&grpc_static_metadata_refcounts[24], {{g_bytes + 302, 19}}}, - {&grpc_static_metadata_refcounts[25], {{g_bytes + 321, 12}}}, - {&grpc_static_metadata_refcounts[26], {{g_bytes + 333, 30}}}, - {&grpc_static_metadata_refcounts[27], {{g_bytes + 363, 31}}}, - {&grpc_static_metadata_refcounts[28], {{g_bytes + 394, 36}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}, - {&grpc_static_metadata_refcounts[31], {{g_bytes + 441, 11}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 452, 1}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 453, 1}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 454, 1}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}, - {&grpc_static_metadata_refcounts[36], {{g_bytes + 463, 8}}}, - {&grpc_static_metadata_refcounts[37], {{g_bytes + 471, 16}}}, - {&grpc_static_metadata_refcounts[38], {{g_bytes + 487, 4}}}, - {&grpc_static_metadata_refcounts[39], {{g_bytes + 491, 3}}}, - {&grpc_static_metadata_refcounts[40], {{g_bytes + 494, 3}}}, - {&grpc_static_metadata_refcounts[41], {{g_bytes + 497, 4}}}, - {&grpc_static_metadata_refcounts[42], {{g_bytes + 501, 5}}}, - {&grpc_static_metadata_refcounts[43], {{g_bytes + 506, 4}}}, - {&grpc_static_metadata_refcounts[44], {{g_bytes + 510, 3}}}, - {&grpc_static_metadata_refcounts[45], {{g_bytes + 513, 3}}}, - {&grpc_static_metadata_refcounts[46], {{g_bytes + 516, 1}}}, - {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 11}}}, - {&grpc_static_metadata_refcounts[48], {{g_bytes + 528, 3}}}, - {&grpc_static_metadata_refcounts[49], {{g_bytes + 531, 3}}}, - {&grpc_static_metadata_refcounts[50], {{g_bytes + 534, 3}}}, - {&grpc_static_metadata_refcounts[51], {{g_bytes + 537, 3}}}, - {&grpc_static_metadata_refcounts[52], {{g_bytes + 540, 3}}}, - {&grpc_static_metadata_refcounts[53], {{g_bytes + 543, 14}}}, - {&grpc_static_metadata_refcounts[54], {{g_bytes + 557, 13}}}, - {&grpc_static_metadata_refcounts[55], {{g_bytes + 570, 15}}}, - {&grpc_static_metadata_refcounts[56], {{g_bytes + 585, 13}}}, - {&grpc_static_metadata_refcounts[57], {{g_bytes + 598, 6}}}, - {&grpc_static_metadata_refcounts[58], {{g_bytes + 604, 27}}}, - {&grpc_static_metadata_refcounts[59], {{g_bytes + 631, 3}}}, - {&grpc_static_metadata_refcounts[60], {{g_bytes + 634, 5}}}, - {&grpc_static_metadata_refcounts[61], {{g_bytes + 639, 13}}}, - {&grpc_static_metadata_refcounts[62], {{g_bytes + 652, 13}}}, - {&grpc_static_metadata_refcounts[63], {{g_bytes + 665, 19}}}, - {&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 16}}}, - {&grpc_static_metadata_refcounts[65], {{g_bytes + 700, 14}}}, - {&grpc_static_metadata_refcounts[66], {{g_bytes + 714, 16}}}, - {&grpc_static_metadata_refcounts[67], {{g_bytes + 730, 13}}}, - {&grpc_static_metadata_refcounts[68], {{g_bytes + 743, 6}}}, - {&grpc_static_metadata_refcounts[69], {{g_bytes + 749, 4}}}, - {&grpc_static_metadata_refcounts[70], {{g_bytes + 753, 4}}}, - {&grpc_static_metadata_refcounts[71], {{g_bytes + 757, 6}}}, - {&grpc_static_metadata_refcounts[72], {{g_bytes + 763, 7}}}, - {&grpc_static_metadata_refcounts[73], {{g_bytes + 770, 4}}}, - {&grpc_static_metadata_refcounts[74], {{g_bytes + 774, 8}}}, - {&grpc_static_metadata_refcounts[75], {{g_bytes + 782, 17}}}, - {&grpc_static_metadata_refcounts[76], {{g_bytes + 799, 13}}}, - {&grpc_static_metadata_refcounts[77], {{g_bytes + 812, 8}}}, - {&grpc_static_metadata_refcounts[78], {{g_bytes + 820, 19}}}, - {&grpc_static_metadata_refcounts[79], {{g_bytes + 839, 13}}}, - {&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 11}}}, - {&grpc_static_metadata_refcounts[81], {{g_bytes + 863, 4}}}, - {&grpc_static_metadata_refcounts[82], {{g_bytes + 867, 8}}}, - {&grpc_static_metadata_refcounts[83], {{g_bytes + 875, 12}}}, - {&grpc_static_metadata_refcounts[84], {{g_bytes + 887, 18}}}, - {&grpc_static_metadata_refcounts[85], {{g_bytes + 905, 19}}}, - {&grpc_static_metadata_refcounts[86], {{g_bytes + 924, 5}}}, - {&grpc_static_metadata_refcounts[87], {{g_bytes + 929, 7}}}, - {&grpc_static_metadata_refcounts[88], {{g_bytes + 936, 7}}}, - {&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 11}}}, - {&grpc_static_metadata_refcounts[90], {{g_bytes + 954, 6}}}, - {&grpc_static_metadata_refcounts[91], {{g_bytes + 960, 10}}}, - {&grpc_static_metadata_refcounts[92], {{g_bytes + 970, 25}}}, - {&grpc_static_metadata_refcounts[93], {{g_bytes + 995, 17}}}, - {&grpc_static_metadata_refcounts[94], {{g_bytes + 1012, 4}}}, - {&grpc_static_metadata_refcounts[95], {{g_bytes + 1016, 3}}}, - {&grpc_static_metadata_refcounts[96], {{g_bytes + 1019, 16}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1035, 16}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}, - {&grpc_static_metadata_refcounts[99], {{g_bytes + 1064, 12}}}, - {&grpc_static_metadata_refcounts[100], {{g_bytes + 1076, 21}}}, + {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 26}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 316, 22}}}, + {&grpc_static_metadata_refcounts[24], {{g_bytes + 338, 12}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}, + {&grpc_static_metadata_refcounts[27], {{g_bytes + 352, 1}}}, + {&grpc_static_metadata_refcounts[28], {{g_bytes + 353, 1}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}, + {&grpc_static_metadata_refcounts[30], {{g_bytes + 354, 19}}}, + {&grpc_static_metadata_refcounts[31], {{g_bytes + 373, 12}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 385, 30}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 415, 31}}}, + {&grpc_static_metadata_refcounts[34], {{g_bytes + 446, 36}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 493, 11}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 504, 1}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 513, 8}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 521, 16}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 537, 4}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 541, 3}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 544, 3}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 547, 4}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 551, 5}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 556, 4}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 560, 3}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 563, 3}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 566, 1}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 567, 11}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 578, 3}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 581, 3}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 584, 3}}}, + {&grpc_static_metadata_refcounts[55], {{g_bytes + 587, 3}}}, + {&grpc_static_metadata_refcounts[56], {{g_bytes + 590, 3}}}, + {&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 14}}}, + {&grpc_static_metadata_refcounts[58], {{g_bytes + 607, 13}}}, + {&grpc_static_metadata_refcounts[59], {{g_bytes + 620, 15}}}, + {&grpc_static_metadata_refcounts[60], {{g_bytes + 635, 13}}}, + {&grpc_static_metadata_refcounts[61], {{g_bytes + 648, 6}}}, + {&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 27}}}, + {&grpc_static_metadata_refcounts[63], {{g_bytes + 681, 3}}}, + {&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 5}}}, + {&grpc_static_metadata_refcounts[65], {{g_bytes + 689, 13}}}, + {&grpc_static_metadata_refcounts[66], {{g_bytes + 702, 13}}}, + {&grpc_static_metadata_refcounts[67], {{g_bytes + 715, 19}}}, + {&grpc_static_metadata_refcounts[68], {{g_bytes + 734, 16}}}, + {&grpc_static_metadata_refcounts[69], {{g_bytes + 750, 14}}}, + {&grpc_static_metadata_refcounts[70], {{g_bytes + 764, 16}}}, + {&grpc_static_metadata_refcounts[71], {{g_bytes + 780, 13}}}, + {&grpc_static_metadata_refcounts[72], {{g_bytes + 793, 6}}}, + {&grpc_static_metadata_refcounts[73], {{g_bytes + 799, 4}}}, + {&grpc_static_metadata_refcounts[74], {{g_bytes + 803, 4}}}, + {&grpc_static_metadata_refcounts[75], {{g_bytes + 807, 6}}}, + {&grpc_static_metadata_refcounts[76], {{g_bytes + 813, 7}}}, + {&grpc_static_metadata_refcounts[77], {{g_bytes + 820, 4}}}, + {&grpc_static_metadata_refcounts[78], {{g_bytes + 824, 8}}}, + {&grpc_static_metadata_refcounts[79], {{g_bytes + 832, 17}}}, + {&grpc_static_metadata_refcounts[80], {{g_bytes + 849, 13}}}, + {&grpc_static_metadata_refcounts[81], {{g_bytes + 862, 8}}}, + {&grpc_static_metadata_refcounts[82], {{g_bytes + 870, 19}}}, + {&grpc_static_metadata_refcounts[83], {{g_bytes + 889, 13}}}, + {&grpc_static_metadata_refcounts[84], {{g_bytes + 902, 11}}}, + {&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 4}}}, + {&grpc_static_metadata_refcounts[86], {{g_bytes + 917, 8}}}, + {&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 12}}}, + {&grpc_static_metadata_refcounts[88], {{g_bytes + 937, 18}}}, + {&grpc_static_metadata_refcounts[89], {{g_bytes + 955, 19}}}, + {&grpc_static_metadata_refcounts[90], {{g_bytes + 974, 5}}}, + {&grpc_static_metadata_refcounts[91], {{g_bytes + 979, 7}}}, + {&grpc_static_metadata_refcounts[92], {{g_bytes + 986, 7}}}, + {&grpc_static_metadata_refcounts[93], {{g_bytes + 993, 11}}}, + {&grpc_static_metadata_refcounts[94], {{g_bytes + 1004, 6}}}, + {&grpc_static_metadata_refcounts[95], {{g_bytes + 1010, 10}}}, + {&grpc_static_metadata_refcounts[96], {{g_bytes + 1020, 25}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1045, 17}}}, + {&grpc_static_metadata_refcounts[98], {{g_bytes + 1062, 4}}}, + {&grpc_static_metadata_refcounts[99], {{g_bytes + 1066, 3}}}, + {&grpc_static_metadata_refcounts[100], {{g_bytes + 1069, 16}}}, + {&grpc_static_metadata_refcounts[101], {{g_bytes + 1085, 16}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}, + {&grpc_static_metadata_refcounts[103], {{g_bytes + 1114, 12}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1126, 21}}}, }; uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { @@ -330,50 +341,51 @@ uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4}; static const int8_t elems_r[] = { - 13, 2, 1, 0, 15, 4, 0, 21, 0, 23, -3, 0, 0, 0, 10, 19, -4, - 0, 0, 1, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, -52, 0, -55, -36, -57, -58, -58, -58, 0, 40, 39, 38, 37, 36, 35, - 34, 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, - 18, 17, 16, 15, 18, 17, 16, 15, 14, 13, 12, 11, 11, 0}; + 16, 11, -1, 0, 15, 2, -78, 24, 0, 18, -5, 0, 0, 0, 17, 14, -8, 0, + 0, 27, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -64, 0, -44, -43, -70, 0, 34, 33, 33, 32, 31, 30, 29, 28, 27, + 27, 26, 25, 24, 23, 22, 21, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, + 11, 14, 13, 12, 11, 10, 9, 9, 8, 7, 6, 5, 0}; static uint32_t elems_phash(uint32_t i) { - i -= 46; - uint32_t x = i % 99; - uint32_t y = i / 99; + i -= 50; + uint32_t x = i % 103; + uint32_t y = i / 103; uint32_t h = x; if (y < GPR_ARRAY_SIZE(elems_r)) { - uint32_t delta = static_cast<uint32_t>(elems_r[y]); + uint32_t delta = (uint32_t)elems_r[y]; h += delta; } return h; } static const uint16_t elem_keys[] = { - 1039, 1040, 145, 146, 541, 1639, 1045, 250, 251, 252, 253, 254, - 1646, 46, 47, 1437, 1942, 1651, 445, 446, 447, 739, 740, 741, - 938, 939, 1538, 2043, 2144, 1451, 944, 5376, 5578, 1545, 5780, 5881, - 1670, 5982, 1550, 6083, 6184, 6285, 6386, 6487, 6588, 6689, 6790, 6891, - 6992, 7093, 7194, 7295, 7396, 5679, 7497, 7598, 7699, 7800, 7901, 8002, - 8103, 8204, 8305, 8406, 8507, 8608, 8709, 8810, 1107, 1108, 1109, 1110, - 8911, 9012, 9113, 9214, 9315, 9416, 9517, 9618, 1714, 9719, 0, 326, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 241, 242, 0, 0, 0, 0, 0, 0, 139, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0}; + 1085, 1086, 565, 1709, 1089, 262, 263, 264, 265, 266, 1716, + 153, 154, 1719, 760, 761, 50, 51, 465, 466, 467, 980, + 981, 1604, 1499, 984, 773, 2129, 2234, 6014, 1611, 6434, 1738, + 1614, 6539, 6644, 1511, 6749, 6854, 6959, 7064, 7169, 7274, 7379, + 2024, 7484, 7589, 7694, 7799, 7904, 8009, 8114, 8219, 6224, 8324, + 8429, 6329, 8534, 8639, 8744, 8849, 8954, 9059, 9164, 9269, 9374, + 1151, 1152, 1153, 1154, 9479, 9584, 9689, 9794, 9899, 10004, 1782, + 10109, 10214, 10319, 10424, 10529, 0, 0, 0, 0, 0, 344, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 253, 254, 147, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0}; static const uint8_t elem_idxs[] = { - 77, 79, 15, 16, 6, 25, 76, 19, 20, 21, 22, 23, 84, 17, - 18, 43, 72, 83, 11, 12, 13, 0, 1, 2, 5, 4, 38, 50, - 57, 7, 3, 24, 27, 37, 29, 30, 26, 31, 36, 32, 33, 34, - 35, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 28, 51, 52, - 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 78, 80, - 81, 82, 66, 67, 68, 69, 70, 71, 73, 74, 85, 75, 255, 14, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 9, 10, 255, 255, 255, 255, 255, 255, 8}; + 77, 79, 6, 25, 76, 19, 20, 21, 22, 23, 84, 15, 16, 83, 1, + 2, 17, 18, 11, 12, 13, 5, 4, 38, 43, 3, 0, 50, 57, 24, + 37, 29, 26, 36, 30, 31, 7, 32, 33, 34, 35, 39, 40, 41, 72, + 42, 44, 45, 46, 47, 48, 49, 51, 27, 52, 53, 28, 54, 55, 56, + 58, 59, 60, 61, 62, 63, 78, 80, 81, 82, 64, 65, 66, 67, 68, + 69, 85, 70, 71, 73, 74, 75, 255, 255, 255, 255, 255, 14, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 9, 10, 8}; grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { if (a == -1 || b == -1) return GRPC_MDNULL; - uint32_t k = static_cast<uint32_t>(a * 101 + b); + uint32_t k = (uint32_t)(a * 105 + b); uint32_t h = elems_phash(k); return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && elem_idxs[h] != 255 @@ -384,177 +396,177 @@ grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 452, 1}}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 504, 1}}}}, {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 453, 1}}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}}, {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 454, 1}}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}}, {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}}, {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, - {&grpc_static_metadata_refcounts[36], {{g_bytes + 463, 8}}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 513, 8}}}}, {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[37], {{g_bytes + 471, 16}}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 521, 16}}}}, {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[38], {{g_bytes + 487, 4}}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 537, 4}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[39], {{g_bytes + 491, 3}}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 541, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[40], {{g_bytes + 494, 3}}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 544, 3}}}}, {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[41], {{g_bytes + 497, 4}}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 547, 4}}}}, {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[42], {{g_bytes + 501, 5}}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 551, 5}}}}, {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[43], {{g_bytes + 506, 4}}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 556, 4}}}}, {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[44], {{g_bytes + 510, 3}}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 560, 3}}}}, {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[45], {{g_bytes + 513, 3}}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 563, 3}}}}, {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[46], {{g_bytes + 516, 1}}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 566, 1}}}}, {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 11}}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 567, 11}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[48], {{g_bytes + 528, 3}}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 578, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[49], {{g_bytes + 531, 3}}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 581, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[50], {{g_bytes + 534, 3}}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 584, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[51], {{g_bytes + 537, 3}}}}, + {&grpc_static_metadata_refcounts[55], {{g_bytes + 587, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[52], {{g_bytes + 540, 3}}}}, - {{&grpc_static_metadata_refcounts[53], {{g_bytes + 543, 14}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[56], {{g_bytes + 590, 3}}}}, + {{&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 14}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[54], {{g_bytes + 557, 13}}}}, - {{&grpc_static_metadata_refcounts[55], {{g_bytes + 570, 15}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[56], {{g_bytes + 585, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[57], {{g_bytes + 598, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[58], {{g_bytes + 604, 27}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[59], {{g_bytes + 631, 3}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[60], {{g_bytes + 634, 5}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[61], {{g_bytes + 639, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[62], {{g_bytes + 652, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[63], {{g_bytes + 665, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[58], {{g_bytes + 607, 13}}}}, + {{&grpc_static_metadata_refcounts[59], {{g_bytes + 620, 15}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[60], {{g_bytes + 635, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[61], {{g_bytes + 648, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 27}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[63], {{g_bytes + 681, 3}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 5}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[65], {{g_bytes + 689, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[66], {{g_bytes + 702, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[67], {{g_bytes + 715, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[65], {{g_bytes + 700, 14}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[66], {{g_bytes + 714, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[67], {{g_bytes + 730, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[68], {{g_bytes + 734, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[69], {{g_bytes + 750, 14}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[70], {{g_bytes + 764, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[71], {{g_bytes + 780, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[68], {{g_bytes + 743, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[69], {{g_bytes + 749, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[70], {{g_bytes + 753, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[71], {{g_bytes + 757, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[72], {{g_bytes + 763, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[73], {{g_bytes + 770, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[72], {{g_bytes + 793, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[73], {{g_bytes + 799, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[74], {{g_bytes + 803, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[75], {{g_bytes + 807, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[76], {{g_bytes + 813, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[77], {{g_bytes + 820, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[74], {{g_bytes + 774, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[75], {{g_bytes + 782, 17}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[76], {{g_bytes + 799, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[77], {{g_bytes + 812, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[78], {{g_bytes + 820, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[79], {{g_bytes + 839, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[78], {{g_bytes + 824, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[79], {{g_bytes + 832, 17}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[80], {{g_bytes + 849, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[81], {{g_bytes + 862, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[82], {{g_bytes + 870, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[83], {{g_bytes + 889, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 11}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[81], {{g_bytes + 863, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[82], {{g_bytes + 867, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[83], {{g_bytes + 875, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[84], {{g_bytes + 887, 18}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[85], {{g_bytes + 905, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[86], {{g_bytes + 924, 5}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[87], {{g_bytes + 929, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[88], {{g_bytes + 936, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 11}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[90], {{g_bytes + 954, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[91], {{g_bytes + 960, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[92], {{g_bytes + 970, 25}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[93], {{g_bytes + 995, 17}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[84], {{g_bytes + 902, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[86], {{g_bytes + 917, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 12}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[88], {{g_bytes + 937, 18}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[89], {{g_bytes + 955, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[90], {{g_bytes + 974, 5}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[91], {{g_bytes + 979, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[92], {{g_bytes + 986, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[93], {{g_bytes + 993, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1004, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1010, 10}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[96], {{g_bytes + 1020, 25}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[97], {{g_bytes + 1045, 17}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1012, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1016, 3}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[96], {{g_bytes + 1019, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[98], {{g_bytes + 1062, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[99], {{g_bytes + 1066, 3}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[100], {{g_bytes + 1069, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1035, 16}}}}, + {&grpc_static_metadata_refcounts[101], {{g_bytes + 1085, 16}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[99], {{g_bytes + 1064, 12}}}}, + {&grpc_static_metadata_refcounts[103], {{g_bytes + 1114, 12}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[100], {{g_bytes + 1076, 21}}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1126, 21}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}}, }; bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { true, // :path @@ -579,6 +591,8 @@ bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { true, // user-agent true, // host true, // lb-token + true, // grpc-previous-rpc-attempts + true, // grpc-retry-pushback-ms }; const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 76, 77, 78, diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h index 88d9f9f52c..b3a10f5873 100644 --- a/src/core/lib/transport/static_metadata.h +++ b/src/core/lib/transport/static_metadata.h @@ -31,7 +31,7 @@ #include "src/core/lib/transport/metadata.h" -#define GRPC_STATIC_MDSTR_COUNT 101 +#define GRPC_STATIC_MDSTR_COUNT 105 extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]; /* ":path" */ #define GRPC_MDSTR_PATH (grpc_static_slice_table[0]) @@ -78,168 +78,176 @@ extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]; #define GRPC_MDSTR_HOST (grpc_static_slice_table[20]) /* "lb-token" */ #define GRPC_MDSTR_LB_TOKEN (grpc_static_slice_table[21]) +/* "grpc-previous-rpc-attempts" */ +#define GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS (grpc_static_slice_table[22]) +/* "grpc-retry-pushback-ms" */ +#define GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS (grpc_static_slice_table[23]) /* "grpc-timeout" */ -#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[22]) +#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[24]) +/* "1" */ +#define GRPC_MDSTR_1 (grpc_static_slice_table[25]) +/* "2" */ +#define GRPC_MDSTR_2 (grpc_static_slice_table[26]) +/* "3" */ +#define GRPC_MDSTR_3 (grpc_static_slice_table[27]) +/* "4" */ +#define GRPC_MDSTR_4 (grpc_static_slice_table[28]) /* "" */ -#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[23]) +#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[29]) /* "grpc.wait_for_ready" */ -#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[24]) +#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[30]) /* "grpc.timeout" */ -#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[25]) +#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[31]) /* "grpc.max_request_message_bytes" */ #define GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES \ - (grpc_static_slice_table[26]) + (grpc_static_slice_table[32]) /* "grpc.max_response_message_bytes" */ #define GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES \ - (grpc_static_slice_table[27]) + (grpc_static_slice_table[33]) /* "/grpc.lb.v1.LoadBalancer/BalanceLoad" */ #define GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD \ - (grpc_static_slice_table[28]) + (grpc_static_slice_table[34]) /* "deflate" */ -#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[29]) +#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[35]) /* "gzip" */ -#define GRPC_MDSTR_GZIP (grpc_static_slice_table[30]) +#define GRPC_MDSTR_GZIP (grpc_static_slice_table[36]) /* "stream/gzip" */ -#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[31]) +#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[37]) /* "0" */ -#define GRPC_MDSTR_0 (grpc_static_slice_table[32]) -/* "1" */ -#define GRPC_MDSTR_1 (grpc_static_slice_table[33]) -/* "2" */ -#define GRPC_MDSTR_2 (grpc_static_slice_table[34]) +#define GRPC_MDSTR_0 (grpc_static_slice_table[38]) /* "identity" */ -#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[35]) +#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[39]) /* "trailers" */ -#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[36]) +#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[40]) /* "application/grpc" */ -#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[37]) +#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[41]) /* "POST" */ -#define GRPC_MDSTR_POST (grpc_static_slice_table[38]) +#define GRPC_MDSTR_POST (grpc_static_slice_table[42]) /* "200" */ -#define GRPC_MDSTR_200 (grpc_static_slice_table[39]) +#define GRPC_MDSTR_200 (grpc_static_slice_table[43]) /* "404" */ -#define GRPC_MDSTR_404 (grpc_static_slice_table[40]) +#define GRPC_MDSTR_404 (grpc_static_slice_table[44]) /* "http" */ -#define GRPC_MDSTR_HTTP (grpc_static_slice_table[41]) +#define GRPC_MDSTR_HTTP (grpc_static_slice_table[45]) /* "https" */ -#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[42]) +#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[46]) /* "grpc" */ -#define GRPC_MDSTR_GRPC (grpc_static_slice_table[43]) +#define GRPC_MDSTR_GRPC (grpc_static_slice_table[47]) /* "GET" */ -#define GRPC_MDSTR_GET (grpc_static_slice_table[44]) +#define GRPC_MDSTR_GET (grpc_static_slice_table[48]) /* "PUT" */ -#define GRPC_MDSTR_PUT (grpc_static_slice_table[45]) +#define GRPC_MDSTR_PUT (grpc_static_slice_table[49]) /* "/" */ -#define GRPC_MDSTR_SLASH (grpc_static_slice_table[46]) +#define GRPC_MDSTR_SLASH (grpc_static_slice_table[50]) /* "/index.html" */ -#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[47]) +#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[51]) /* "204" */ -#define GRPC_MDSTR_204 (grpc_static_slice_table[48]) +#define GRPC_MDSTR_204 (grpc_static_slice_table[52]) /* "206" */ -#define GRPC_MDSTR_206 (grpc_static_slice_table[49]) +#define GRPC_MDSTR_206 (grpc_static_slice_table[53]) /* "304" */ -#define GRPC_MDSTR_304 (grpc_static_slice_table[50]) +#define GRPC_MDSTR_304 (grpc_static_slice_table[54]) /* "400" */ -#define GRPC_MDSTR_400 (grpc_static_slice_table[51]) +#define GRPC_MDSTR_400 (grpc_static_slice_table[55]) /* "500" */ -#define GRPC_MDSTR_500 (grpc_static_slice_table[52]) +#define GRPC_MDSTR_500 (grpc_static_slice_table[56]) /* "accept-charset" */ -#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[53]) +#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[57]) /* "gzip, deflate" */ -#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[54]) +#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[58]) /* "accept-language" */ -#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[55]) +#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[59]) /* "accept-ranges" */ -#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[56]) +#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[60]) /* "accept" */ -#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[57]) +#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[61]) /* "access-control-allow-origin" */ -#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[58]) +#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[62]) /* "age" */ -#define GRPC_MDSTR_AGE (grpc_static_slice_table[59]) +#define GRPC_MDSTR_AGE (grpc_static_slice_table[63]) /* "allow" */ -#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[60]) +#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[64]) /* "authorization" */ -#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[61]) +#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[65]) /* "cache-control" */ -#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[62]) +#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[66]) /* "content-disposition" */ -#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[63]) +#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[67]) /* "content-language" */ -#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[64]) +#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[68]) /* "content-length" */ -#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[65]) +#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[69]) /* "content-location" */ -#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[66]) +#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[70]) /* "content-range" */ -#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[67]) +#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[71]) /* "cookie" */ -#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[68]) +#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[72]) /* "date" */ -#define GRPC_MDSTR_DATE (grpc_static_slice_table[69]) +#define GRPC_MDSTR_DATE (grpc_static_slice_table[73]) /* "etag" */ -#define GRPC_MDSTR_ETAG (grpc_static_slice_table[70]) +#define GRPC_MDSTR_ETAG (grpc_static_slice_table[74]) /* "expect" */ -#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[71]) +#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[75]) /* "expires" */ -#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[72]) +#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[76]) /* "from" */ -#define GRPC_MDSTR_FROM (grpc_static_slice_table[73]) +#define GRPC_MDSTR_FROM (grpc_static_slice_table[77]) /* "if-match" */ -#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[74]) +#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[78]) /* "if-modified-since" */ -#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[75]) +#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[79]) /* "if-none-match" */ -#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[76]) +#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[80]) /* "if-range" */ -#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[77]) +#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[81]) /* "if-unmodified-since" */ -#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[78]) +#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[82]) /* "last-modified" */ -#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[79]) +#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[83]) /* "lb-cost-bin" */ -#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[80]) +#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[84]) /* "link" */ -#define GRPC_MDSTR_LINK (grpc_static_slice_table[81]) +#define GRPC_MDSTR_LINK (grpc_static_slice_table[85]) /* "location" */ -#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[82]) +#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[86]) /* "max-forwards" */ -#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[83]) +#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[87]) /* "proxy-authenticate" */ -#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[84]) +#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[88]) /* "proxy-authorization" */ -#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[85]) +#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[89]) /* "range" */ -#define GRPC_MDSTR_RANGE (grpc_static_slice_table[86]) +#define GRPC_MDSTR_RANGE (grpc_static_slice_table[90]) /* "referer" */ -#define GRPC_MDSTR_REFERER (grpc_static_slice_table[87]) +#define GRPC_MDSTR_REFERER (grpc_static_slice_table[91]) /* "refresh" */ -#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[88]) +#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[92]) /* "retry-after" */ -#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[89]) +#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[93]) /* "server" */ -#define GRPC_MDSTR_SERVER (grpc_static_slice_table[90]) +#define GRPC_MDSTR_SERVER (grpc_static_slice_table[94]) /* "set-cookie" */ -#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[91]) +#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[95]) /* "strict-transport-security" */ -#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[92]) +#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[96]) /* "transfer-encoding" */ -#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[93]) +#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[97]) /* "vary" */ -#define GRPC_MDSTR_VARY (grpc_static_slice_table[94]) +#define GRPC_MDSTR_VARY (grpc_static_slice_table[98]) /* "via" */ -#define GRPC_MDSTR_VIA (grpc_static_slice_table[95]) +#define GRPC_MDSTR_VIA (grpc_static_slice_table[99]) /* "www-authenticate" */ -#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[96]) +#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[100]) /* "identity,deflate" */ -#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[97]) +#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[101]) /* "identity,gzip" */ -#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[98]) +#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[102]) /* "deflate,gzip" */ -#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[99]) +#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[103]) /* "identity,deflate,gzip" */ #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ - (grpc_static_slice_table[100]) + (grpc_static_slice_table[104]) extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable; extern grpc_slice_refcount @@ -537,6 +545,8 @@ typedef enum { GRPC_BATCH_USER_AGENT, GRPC_BATCH_HOST, GRPC_BATCH_LB_TOKEN, + GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS, + GRPC_BATCH_GRPC_RETRY_PUSHBACK_MS, GRPC_BATCH_CALLOUTS_COUNT } grpc_metadata_batch_callouts_index; @@ -565,6 +575,8 @@ typedef union { struct grpc_linked_mdelem* user_agent; struct grpc_linked_mdelem* host; struct grpc_linked_mdelem* lb_token; + struct grpc_linked_mdelem* grpc_previous_rpc_attempts; + struct grpc_linked_mdelem* grpc_retry_pushback_ms; } named; } grpc_metadata_batch_callouts; diff --git a/src/core/lib/transport/status_metadata.cc b/src/core/lib/transport/status_metadata.cc new file mode 100644 index 0000000000..f896053e4d --- /dev/null +++ b/src/core/lib/transport/status_metadata.cc @@ -0,0 +1,54 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/transport/status_metadata.h" + +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +/* we offset status by a small amount when storing it into transport metadata + as metadata cannot store a 0 value (which is used as OK for grpc_status_codes + */ +#define STATUS_OFFSET 1 + +static void destroy_status(void* ignored) {} + +grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md) { + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + return GRPC_STATUS_OK; + } + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) { + return GRPC_STATUS_CANCELLED; + } + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) { + return GRPC_STATUS_UNKNOWN; + } + void* user_data = grpc_mdelem_get_user_data(md, destroy_status); + if (user_data != nullptr) { + return static_cast<grpc_status_code>((intptr_t)user_data - STATUS_OFFSET); + } + uint32_t status; + if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { + status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ + } + grpc_mdelem_set_user_data( + md, destroy_status, (void*)static_cast<intptr_t>(status + STATUS_OFFSET)); + return static_cast<grpc_status_code>(status); +} diff --git a/src/core/lib/iomgr/timer_uv.h b/src/core/lib/transport/status_metadata.h index 093b2d085d..aed9c7ac20 100644 --- a/src/core/lib/iomgr/timer_uv.h +++ b/src/core/lib/transport/status_metadata.h @@ -1,6 +1,6 @@ /* * - * Copyright 2016 gRPC authors. + * Copyright 2017 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,15 @@ * */ -#ifndef GRPC_CORE_LIB_IOMGR_TIMER_UV_H -#define GRPC_CORE_LIB_IOMGR_TIMER_UV_H +#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H +#define GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H #include <grpc/support/port_platform.h> -#include "src/core/lib/iomgr/exec_ctx.h" +#include <grpc/status.h> -struct grpc_timer { - grpc_closure* closure; - /* This is actually a uv_timer_t*, but we want to keep platform-specific - types out of headers */ - void* uv_timer; - int pending; -}; +#include "src/core/lib/transport/metadata.h" -#endif /* GRPC_CORE_LIB_IOMGR_TIMER_UV_H */ +grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md); + +#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H */ diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc index c90d16fc32..6b41e4b37e 100644 --- a/src/core/lib/transport/transport.cc +++ b/src/core/lib/transport/transport.cc @@ -209,7 +209,7 @@ void grpc_transport_stream_op_batch_finish_with_failure( grpc_transport_stream_op_batch* batch, grpc_error* error, grpc_call_combiner* call_combiner) { if (batch->send_message) { - grpc_byte_stream_destroy(batch->payload->send_message.send_message); + batch->payload->send_message.send_message.reset(); } if (batch->recv_message) { GRPC_CALL_COMBINER_START( diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h index b279ce8c80..10e9df0f7c 100644 --- a/src/core/lib/transport/transport.h +++ b/src/core/lib/transport/transport.h @@ -98,6 +98,19 @@ void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from, void grpc_transport_move_stats(grpc_transport_stream_stats* from, grpc_transport_stream_stats* to); +// This struct (which is present in both grpc_transport_stream_op_batch +// and grpc_transport_op_batch) is a convenience to allow filters or +// transports to schedule a closure related to a particular batch without +// having to allocate memory. The general pattern is to initialize the +// closure with the callback arg set to the batch and extra_arg set to +// whatever state is associated with the handler (e.g., the call element +// or the transport stream object). +// +// Note that this can only be used by the current handler of a given +// batch on the way down the stack (i.e., whichever filter or transport is +// currently handling the batch). Once a filter or transport passes control +// of the batch to the next handler, it cannot depend on the contents of +// this struct anymore, because the next handler may reuse it. typedef struct { void* extra_arg; grpc_closure closure; @@ -157,6 +170,11 @@ struct grpc_transport_stream_op_batch_payload { uint32_t send_initial_metadata_flags; // If non-NULL, will be set by the transport to the peer string // (a char*, which the caller takes ownership of). + // Note: This pointer may be used by the transport after the + // send_initial_metadata op is completed. It must remain valid + // until the call is destroyed. + // Note: When a transport sets this, it must free the previous + // value, if any. gpr_atm* peer_string; } send_initial_metadata; @@ -166,15 +184,17 @@ struct grpc_transport_stream_op_batch_payload { struct { // The transport (or a filter that decides to return a failure before - // the op gets down to the transport) is responsible for calling - // grpc_byte_stream_destroy() on this. + // the op gets down to the transport) takes ownership. // The batch's on_complete will not be called until after the byte - // stream is destroyed. - grpc_byte_stream* send_message; + // stream is orphaned. + grpc_core::OrphanablePtr<grpc_core::ByteStream> send_message; } send_message; struct { grpc_metadata_batch* recv_initial_metadata; + // Flags are used only on the server side. If non-null, will be set to + // a bitfield of the GRPC_INITIAL_METADATA_xxx macros (e.g., to + // indicate if the call is idempotent). uint32_t* recv_flags; /** Should be enqueued when initial metadata is ready to be processed. */ grpc_closure* recv_initial_metadata_ready; @@ -184,15 +204,19 @@ struct grpc_transport_stream_op_batch_payload { bool* trailing_metadata_available; // If non-NULL, will be set by the transport to the peer string // (a char*, which the caller takes ownership of). + // Note: This pointer may be used by the transport after the + // recv_initial_metadata op is completed. It must remain valid + // until the call is destroyed. + // Note: When a transport sets this, it must free the previous + // value, if any. gpr_atm* peer_string; } recv_initial_metadata; struct { // Will be set by the transport to point to the byte stream // containing a received message. - // The caller is responsible for calling grpc_byte_stream_destroy() - // on this byte stream. - grpc_byte_stream** recv_message; + // Will be NULL if trailing metadata is received instead of a message. + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message; /** Should be enqueued when one message is ready to be processed. */ grpc_closure* recv_message_ready; } recv_message; diff --git a/src/core/lib/transport/transport_op_string.cc b/src/core/lib/transport/transport_op_string.cc index 6898da17ed..99af7c1931 100644 --- a/src/core/lib/transport/transport_op_string.cc +++ b/src/core/lib/transport/transport_op_string.cc @@ -75,9 +75,16 @@ char* grpc_transport_stream_op_batch_string( if (op->send_message) { gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", - op->payload->send_message.send_message->flags, - op->payload->send_message.send_message->length); + if (op->payload->send_message.send_message != nullptr) { + gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", + op->payload->send_message.send_message->flags(), + op->payload->send_message.send_message->length()); + } else { + // This can happen when we check a batch after the transport has + // processed and cleared the send_message op. + tmp = + gpr_strdup("SEND_MESSAGE(flag and length unknown, already orphaned)"); + } gpr_strvec_add(&b, tmp); } |