Merge branch 'master' of https://github.com/grpc/grpc into channel
commit
dffbe4a1eb
150 changed files with 3337 additions and 2132 deletions
@ -0,0 +1,100 @@ |
||||
[MASTER] |
||||
ignore= |
||||
src/python/grpcio/grpc/beta, |
||||
src/python/grpcio/grpc/framework, |
||||
src/python/grpcio/grpc/framework/common, |
||||
src/python/grpcio/grpc/framework/foundation, |
||||
src/python/grpcio/grpc/framework/interfaces, |
||||
|
||||
[VARIABLES] |
||||
|
||||
# TODO(https://github.com/PyCQA/pylint/issues/1345): How does the inspection |
||||
# not include "unused_" and "ignored_" by default? |
||||
dummy-variables-rgx=^ignored_|^unused_ |
||||
|
||||
[DESIGN] |
||||
|
||||
# NOTE(nathaniel): Not particularly attached to this value; it just seems to |
||||
# be what works for us at the moment (excepting the dead-code-walking Beta |
||||
# API). |
||||
max-args=6 |
||||
|
||||
[MISCELLANEOUS] |
||||
|
||||
# NOTE(nathaniel): We are big fans of "TODO(<issue link>): " and |
||||
# "NOTE(<username or issue link>): ". We do not allow "TODO:", |
||||
# "TODO(<username>):", "FIXME:", or anything else. |
||||
notes=FIXME,XXX |
||||
|
||||
[MESSAGES CONTROL] |
||||
|
||||
disable= |
||||
# -- START OF EXAMPLE-SPECIFIC SUPPRESSIONS -- |
||||
no-self-use, |
||||
unused-argument, |
||||
unused-variable, |
||||
# -- END OF EXAMPLE-SPECIFIC SUPPRESSIONS -- |
||||
|
||||
# TODO(https://github.com/PyCQA/pylint/issues/59#issuecomment-283774279): |
||||
# Enable cyclic-import after a 1.7-or-later pylint release that |
||||
# recognizes our disable=cyclic-import suppressions. |
||||
cyclic-import, |
||||
# TODO(https://github.com/grpc/grpc/issues/8622): Enable this after the |
||||
# Beta API is removed. |
||||
duplicate-code, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): Doesn't seem to |
||||
# understand enum and concurrent.futures; look into this later with the |
||||
# latest pylint version. |
||||
import-error, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): Enable this one. |
||||
# Should take a little configuration but not much. |
||||
invalid-name, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): This doesn't seem to |
||||
# work for now? Try with a later pylint? |
||||
locally-disabled, |
||||
# NOTE(nathaniel): What even is this? *Enabling* an inspection results |
||||
# in a warning? How does that encourage more analysis and coverage? |
||||
locally-enabled, |
||||
# NOTE(nathaniel): We don't write doc strings for most private code |
||||
# elements. |
||||
missing-docstring, |
||||
# NOTE(nathaniel): In numeric comparisons it is better to have the |
||||
# lesser (or lesser-or-equal-to) quantity on the left when the |
||||
# expression is true than it is to worry about which is an identifier |
||||
# and which a literal value. |
||||
misplaced-comparison-constant, |
||||
# NOTE(nathaniel): Our completely abstract interface classes don't have |
||||
# constructors. |
||||
no-init, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): Doesn't yet play |
||||
# nicely with some of our code being implemented in Cython. Maybe in a |
||||
# later version? |
||||
no-name-in-module, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): Suppress these where |
||||
# the odd shape of the authentication portion of the API forces them on |
||||
# us and enable everywhere else. |
||||
protected-access, |
||||
# NOTE(nathaniel): Pylint and I will probably never agree on this. |
||||
too-few-public-methods, |
||||
# NOTE(nathaniel): Pylint and I wil probably never agree on this for |
||||
# private classes. For public classes maybe? |
||||
too-many-instance-attributes, |
||||
# NOTE(nathaniel): Some of our modules have a lot of lines... of |
||||
# specification and documentation. Maybe if this were |
||||
# lines-of-code-based we would use it. |
||||
too-many-lines, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): Maybe we could have |
||||
# this one if we extracted just a few more helper functions... |
||||
too-many-nested-blocks, |
||||
# TODO(https://github.com/grpc/grpc/issues/261): Disable unnecessary |
||||
# super-init requirement for abstract class implementations for now. |
||||
super-init-not-called, |
||||
# NOTE(nathaniel): A single statement that always returns program |
||||
# control is better than two statements the first of which sometimes |
||||
# returns program control and the second of which always returns |
||||
# program control. Probably generally, but definitely in the cases of |
||||
# if:/else: and for:/else:. |
||||
useless-else-on-loop, |
||||
no-else-return, |
||||
# NOTE(lidiz): Python 3 make object inheritance default, but not PY2 |
||||
useless-object-inheritance, |
@ -0,0 +1,134 @@ |
||||
/*
|
||||
* |
||||
* 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 <map> |
||||
|
||||
#include <grpcpp/support/client_interceptor.h> |
||||
|
||||
#ifdef BAZEL_BUILD |
||||
#include "examples/protos/keyvaluestore.grpc.pb.h" |
||||
#else |
||||
#include "keyvaluestore.grpc.pb.h" |
||||
#endif |
||||
|
||||
// This is a naive implementation of a cache. A new cache is for each call. For
|
||||
// each new key request, the key is first searched in the map and if found, the
|
||||
// interceptor fills in the return value without making a request to the server.
|
||||
// Only if the key is not found in the cache do we make a request.
|
||||
class CachingInterceptor : public grpc::experimental::Interceptor { |
||||
public: |
||||
CachingInterceptor(grpc::experimental::ClientRpcInfo* info) {} |
||||
|
||||
void Intercept( |
||||
::grpc::experimental::InterceptorBatchMethods* methods) override { |
||||
bool hijack = false; |
||||
if (methods->QueryInterceptionHookPoint( |
||||
grpc::experimental::InterceptionHookPoints:: |
||||
PRE_SEND_INITIAL_METADATA)) { |
||||
// Hijack all calls
|
||||
hijack = true; |
||||
// Create a stream on which this interceptor can make requests
|
||||
stub_ = keyvaluestore::KeyValueStore::NewStub( |
||||
methods->GetInterceptedChannel()); |
||||
stream_ = stub_->GetValues(&context_); |
||||
} |
||||
if (methods->QueryInterceptionHookPoint( |
||||
grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) { |
||||
// We know that clients perform a Read and a Write in a loop, so we don't
|
||||
// need to maintain a list of the responses.
|
||||
std::string requested_key; |
||||
const keyvaluestore::Request* req_msg = |
||||
static_cast<const keyvaluestore::Request*>(methods->GetSendMessage()); |
||||
if (req_msg != nullptr) { |
||||
requested_key = req_msg->key(); |
||||
} else { |
||||
// The non-serialized form would not be available in certain scenarios,
|
||||
// so add a fallback
|
||||
keyvaluestore::Request req_msg; |
||||
auto* buffer = methods->GetSerializedSendMessage(); |
||||
auto copied_buffer = *buffer; |
||||
GPR_ASSERT( |
||||
grpc::SerializationTraits<keyvaluestore::Request>::Deserialize( |
||||
&copied_buffer, &req_msg) |
||||
.ok()); |
||||
requested_key = req_msg.key(); |
||||
} |
||||
|
||||
// Check if the key is present in the map
|
||||
auto search = cached_map_.find(requested_key); |
||||
if (search != cached_map_.end()) { |
||||
std::cout << "Key " << requested_key << "found in map"; |
||||
response_ = search->second; |
||||
} else { |
||||
std::cout << "Key " << requested_key << "not found in cache"; |
||||
// Key was not found in the cache, so make a request
|
||||
keyvaluestore::Request req; |
||||
req.set_key(requested_key); |
||||
stream_->Write(req); |
||||
keyvaluestore::Response resp; |
||||
stream_->Read(&resp); |
||||
response_ = resp.value(); |
||||
// Insert the pair in the cache for future requests
|
||||
cached_map_.insert({requested_key, response_}); |
||||
} |
||||
} |
||||
if (methods->QueryInterceptionHookPoint( |
||||
grpc::experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) { |
||||
stream_->WritesDone(); |
||||
} |
||||
if (methods->QueryInterceptionHookPoint( |
||||
grpc::experimental::InterceptionHookPoints::PRE_RECV_MESSAGE)) { |
||||
keyvaluestore::Response* resp = |
||||
static_cast<keyvaluestore::Response*>(methods->GetRecvMessage()); |
||||
resp->set_value(response_); |
||||
} |
||||
if (methods->QueryInterceptionHookPoint( |
||||
grpc::experimental::InterceptionHookPoints::PRE_RECV_STATUS)) { |
||||
auto* status = methods->GetRecvStatus(); |
||||
*status = grpc::Status::OK; |
||||
} |
||||
// One of Hijack or Proceed always needs to be called to make progress.
|
||||
if (hijack) { |
||||
// Hijack is called only once when PRE_SEND_INITIAL_METADATA is present in
|
||||
// the hook points
|
||||
methods->Hijack(); |
||||
} else { |
||||
// Proceed is an indicator that the interceptor is done intercepting the
|
||||
// batch.
|
||||
methods->Proceed(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
grpc::ClientContext context_; |
||||
std::unique_ptr<keyvaluestore::KeyValueStore::Stub> stub_; |
||||
std::unique_ptr< |
||||
grpc::ClientReaderWriter<keyvaluestore::Request, keyvaluestore::Response>> |
||||
stream_; |
||||
std::map<std::string, std::string> cached_map_; |
||||
std::string response_; |
||||
}; |
||||
|
||||
class CachingInterceptorFactory |
||||
: public grpc::experimental::ClientInterceptorFactoryInterface { |
||||
public: |
||||
grpc::experimental::Interceptor* CreateClientInterceptor( |
||||
grpc::experimental::ClientRpcInfo* info) override { |
||||
return new CachingInterceptor(info); |
||||
} |
||||
}; |
@ -0,0 +1,177 @@ |
||||
//
|
||||
//
|
||||
// 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/ext/filters/client_channel/global_subchannel_pool.h" |
||||
|
||||
#include "src/core/ext/filters/client_channel/subchannel.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
GlobalSubchannelPool::GlobalSubchannelPool() { |
||||
subchannel_map_ = grpc_avl_create(&subchannel_avl_vtable_); |
||||
gpr_mu_init(&mu_); |
||||
} |
||||
|
||||
GlobalSubchannelPool::~GlobalSubchannelPool() { |
||||
gpr_mu_destroy(&mu_); |
||||
grpc_avl_unref(subchannel_map_, nullptr); |
||||
} |
||||
|
||||
void GlobalSubchannelPool::Init() { |
||||
instance_ = New<RefCountedPtr<GlobalSubchannelPool>>( |
||||
MakeRefCounted<GlobalSubchannelPool>()); |
||||
} |
||||
|
||||
void GlobalSubchannelPool::Shutdown() { |
||||
// To ensure Init() was called before.
|
||||
GPR_ASSERT(instance_ != nullptr); |
||||
// To ensure Shutdown() was not called before.
|
||||
GPR_ASSERT(*instance_ != nullptr); |
||||
instance_->reset(); |
||||
Delete(instance_); |
||||
} |
||||
|
||||
RefCountedPtr<GlobalSubchannelPool> GlobalSubchannelPool::instance() { |
||||
GPR_ASSERT(instance_ != nullptr); |
||||
GPR_ASSERT(*instance_ != nullptr); |
||||
return *instance_; |
||||
} |
||||
|
||||
grpc_subchannel* GlobalSubchannelPool::RegisterSubchannel( |
||||
SubchannelKey* key, grpc_subchannel* constructed) { |
||||
grpc_subchannel* c = nullptr; |
||||
// Compare and swap (CAS) loop:
|
||||
while (c == nullptr) { |
||||
// Ref the shared map to have a local copy.
|
||||
gpr_mu_lock(&mu_); |
||||
grpc_avl old_map = grpc_avl_ref(subchannel_map_, nullptr); |
||||
gpr_mu_unlock(&mu_); |
||||
// Check to see if a subchannel already exists.
|
||||
c = static_cast<grpc_subchannel*>(grpc_avl_get(old_map, key, nullptr)); |
||||
if (c != nullptr) { |
||||
// The subchannel already exists. Reuse it.
|
||||
c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(c, "subchannel_register+reuse"); |
||||
GRPC_SUBCHANNEL_UNREF(constructed, "subchannel_register+found_existing"); |
||||
// Exit the CAS loop without modifying the shared map.
|
||||
} else { |
||||
// There hasn't been such subchannel. Add one.
|
||||
// Note that we should ref the old map first because grpc_avl_add() will
|
||||
// unref it while we still need to access it later.
|
||||
grpc_avl new_map = grpc_avl_add( |
||||
grpc_avl_ref(old_map, nullptr), New<SubchannelKey>(*key), |
||||
GRPC_SUBCHANNEL_WEAK_REF(constructed, "subchannel_register+new"), |
||||
nullptr); |
||||
// Try to publish the change to the shared map. It may happen (but
|
||||
// unlikely) that some other thread has changed the shared map, so compare
|
||||
// to make sure it's unchanged before swapping. Retry if it's changed.
|
||||
gpr_mu_lock(&mu_); |
||||
if (old_map.root == subchannel_map_.root) { |
||||
GPR_SWAP(grpc_avl, new_map, subchannel_map_); |
||||
c = constructed; |
||||
} |
||||
gpr_mu_unlock(&mu_); |
||||
grpc_avl_unref(new_map, nullptr); |
||||
} |
||||
grpc_avl_unref(old_map, nullptr); |
||||
} |
||||
return c; |
||||
} |
||||
|
||||
void GlobalSubchannelPool::UnregisterSubchannel(SubchannelKey* key) { |
||||
bool done = false; |
||||
// Compare and swap (CAS) loop:
|
||||
while (!done) { |
||||
// Ref the shared map to have a local copy.
|
||||
gpr_mu_lock(&mu_); |
||||
grpc_avl old_map = grpc_avl_ref(subchannel_map_, nullptr); |
||||
gpr_mu_unlock(&mu_); |
||||
// Remove the subchannel.
|
||||
// Note that we should ref the old map first because grpc_avl_remove() will
|
||||
// unref it while we still need to access it later.
|
||||
grpc_avl new_map = |
||||
grpc_avl_remove(grpc_avl_ref(old_map, nullptr), key, nullptr); |
||||
// Try to publish the change to the shared map. It may happen (but
|
||||
// unlikely) that some other thread has changed the shared map, so compare
|
||||
// to make sure it's unchanged before swapping. Retry if it's changed.
|
||||
gpr_mu_lock(&mu_); |
||||
if (old_map.root == subchannel_map_.root) { |
||||
GPR_SWAP(grpc_avl, new_map, subchannel_map_); |
||||
done = true; |
||||
} |
||||
gpr_mu_unlock(&mu_); |
||||
grpc_avl_unref(new_map, nullptr); |
||||
grpc_avl_unref(old_map, nullptr); |
||||
} |
||||
} |
||||
|
||||
grpc_subchannel* GlobalSubchannelPool::FindSubchannel(SubchannelKey* key) { |
||||
// Lock, and take a reference to the subchannel map.
|
||||
// We don't need to do the search under a lock as AVL's are immutable.
|
||||
gpr_mu_lock(&mu_); |
||||
grpc_avl index = grpc_avl_ref(subchannel_map_, nullptr); |
||||
gpr_mu_unlock(&mu_); |
||||
grpc_subchannel* c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF( |
||||
static_cast<grpc_subchannel*>(grpc_avl_get(index, key, nullptr)), |
||||
"found_from_pool"); |
||||
grpc_avl_unref(index, nullptr); |
||||
return c; |
||||
} |
||||
|
||||
RefCountedPtr<GlobalSubchannelPool>* GlobalSubchannelPool::instance_ = nullptr; |
||||
|
||||
namespace { |
||||
|
||||
void sck_avl_destroy(void* p, void* user_data) { |
||||
SubchannelKey* key = static_cast<SubchannelKey*>(p); |
||||
Delete(key); |
||||
} |
||||
|
||||
void* sck_avl_copy(void* p, void* unused) { |
||||
const SubchannelKey* key = static_cast<const SubchannelKey*>(p); |
||||
auto* new_key = New<SubchannelKey>(*key); |
||||
return static_cast<void*>(new_key); |
||||
} |
||||
|
||||
long sck_avl_compare(void* a, void* b, void* unused) { |
||||
const SubchannelKey* key_a = static_cast<const SubchannelKey*>(a); |
||||
const SubchannelKey* key_b = static_cast<const SubchannelKey*>(b); |
||||
return key_a->Cmp(*key_b); |
||||
} |
||||
|
||||
void scv_avl_destroy(void* p, void* user_data) { |
||||
GRPC_SUBCHANNEL_WEAK_UNREF((grpc_subchannel*)p, "global_subchannel_pool"); |
||||
} |
||||
|
||||
void* scv_avl_copy(void* p, void* unused) { |
||||
GRPC_SUBCHANNEL_WEAK_REF((grpc_subchannel*)p, "global_subchannel_pool"); |
||||
return p; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
const grpc_avl_vtable GlobalSubchannelPool::subchannel_avl_vtable_ = { |
||||
sck_avl_destroy, // destroy_key
|
||||
sck_avl_copy, // copy_key
|
||||
sck_avl_compare, // compare_keys
|
||||
scv_avl_destroy, // destroy_value
|
||||
scv_avl_copy // copy_value
|
||||
}; |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,68 @@ |
||||
/*
|
||||
* |
||||
* 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_EXT_FILTERS_CLIENT_CHANNEL_GLOBAL_SUBCHANNEL_POOL_H |
||||
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_GLOBAL_SUBCHANNEL_POOL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/filters/client_channel/subchannel_pool_interface.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// The global subchannel pool. It shares subchannels among channels. There
|
||||
// should be only one instance of this class. Init() should be called once at
|
||||
// the filter initialization time; Shutdown() should be called once at the
|
||||
// filter shutdown time.
|
||||
// TODO(juanlishen): Enable subchannel retention.
|
||||
class GlobalSubchannelPool final : public SubchannelPoolInterface { |
||||
public: |
||||
// The ctor and dtor are not intended to use directly.
|
||||
GlobalSubchannelPool(); |
||||
~GlobalSubchannelPool() override; |
||||
|
||||
// Should be called exactly once at filter initialization time.
|
||||
static void Init(); |
||||
// Should be called exactly once at filter shutdown time.
|
||||
static void Shutdown(); |
||||
|
||||
// Gets the singleton instance.
|
||||
static RefCountedPtr<GlobalSubchannelPool> instance(); |
||||
|
||||
// Implements interface methods.
|
||||
grpc_subchannel* RegisterSubchannel(SubchannelKey* key, |
||||
grpc_subchannel* constructed) override; |
||||
void UnregisterSubchannel(SubchannelKey* key) override; |
||||
grpc_subchannel* FindSubchannel(SubchannelKey* key) override; |
||||
|
||||
private: |
||||
// The singleton instance. (It's a pointer to RefCountedPtr so that this
|
||||
// non-local static object can be trivially destructible.)
|
||||
static RefCountedPtr<GlobalSubchannelPool>* instance_; |
||||
|
||||
// The vtable for subchannel operations in an AVL tree.
|
||||
static const grpc_avl_vtable subchannel_avl_vtable_; |
||||
// A map from subchannel key to subchannel.
|
||||
grpc_avl subchannel_map_; |
||||
// To protect subchannel_map_.
|
||||
gpr_mu mu_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_GLOBAL_SUBCHANNEL_POOL_H */ |
@ -0,0 +1,96 @@ |
||||
//
|
||||
//
|
||||
// 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/ext/filters/client_channel/local_subchannel_pool.h" |
||||
|
||||
#include "src/core/ext/filters/client_channel/subchannel.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
LocalSubchannelPool::LocalSubchannelPool() { |
||||
subchannel_map_ = grpc_avl_create(&subchannel_avl_vtable_); |
||||
} |
||||
|
||||
LocalSubchannelPool::~LocalSubchannelPool() { |
||||
grpc_avl_unref(subchannel_map_, nullptr); |
||||
} |
||||
|
||||
grpc_subchannel* LocalSubchannelPool::RegisterSubchannel( |
||||
SubchannelKey* key, grpc_subchannel* constructed) { |
||||
// Check to see if a subchannel already exists.
|
||||
grpc_subchannel* c = static_cast<grpc_subchannel*>( |
||||
grpc_avl_get(subchannel_map_, key, nullptr)); |
||||
if (c != nullptr) { |
||||
// The subchannel already exists. Reuse it.
|
||||
c = GRPC_SUBCHANNEL_REF(c, "subchannel_register+reuse"); |
||||
GRPC_SUBCHANNEL_UNREF(constructed, "subchannel_register+found_existing"); |
||||
} else { |
||||
// There hasn't been such subchannel. Add one.
|
||||
subchannel_map_ = grpc_avl_add(subchannel_map_, New<SubchannelKey>(*key), |
||||
constructed, nullptr); |
||||
c = constructed; |
||||
} |
||||
return c; |
||||
} |
||||
|
||||
void LocalSubchannelPool::UnregisterSubchannel(SubchannelKey* key) { |
||||
subchannel_map_ = grpc_avl_remove(subchannel_map_, key, nullptr); |
||||
} |
||||
|
||||
grpc_subchannel* LocalSubchannelPool::FindSubchannel(SubchannelKey* key) { |
||||
grpc_subchannel* c = static_cast<grpc_subchannel*>( |
||||
grpc_avl_get(subchannel_map_, key, nullptr)); |
||||
return c == nullptr ? c : GRPC_SUBCHANNEL_REF(c, "found_from_pool"); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
void sck_avl_destroy(void* p, void* user_data) { |
||||
SubchannelKey* key = static_cast<SubchannelKey*>(p); |
||||
Delete(key); |
||||
} |
||||
|
||||
void* sck_avl_copy(void* p, void* unused) { |
||||
const SubchannelKey* key = static_cast<const SubchannelKey*>(p); |
||||
auto new_key = New<SubchannelKey>(*key); |
||||
return static_cast<void*>(new_key); |
||||
} |
||||
|
||||
long sck_avl_compare(void* a, void* b, void* unused) { |
||||
const SubchannelKey* key_a = static_cast<const SubchannelKey*>(a); |
||||
const SubchannelKey* key_b = static_cast<const SubchannelKey*>(b); |
||||
return key_a->Cmp(*key_b); |
||||
} |
||||
|
||||
void scv_avl_destroy(void* p, void* user_data) {} |
||||
|
||||
void* scv_avl_copy(void* p, void* unused) { return p; } |
||||
|
||||
} // namespace
|
||||
|
||||
const grpc_avl_vtable LocalSubchannelPool::subchannel_avl_vtable_ = { |
||||
sck_avl_destroy, // destroy_key
|
||||
sck_avl_copy, // copy_key
|
||||
sck_avl_compare, // compare_keys
|
||||
scv_avl_destroy, // destroy_value
|
||||
scv_avl_copy // copy_value
|
||||
}; |
||||
|
||||
} // namespace grpc_core
|
@ -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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LOCAL_SUBCHANNEL_POOL_H |
||||
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LOCAL_SUBCHANNEL_POOL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/filters/client_channel/subchannel_pool_interface.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// The local subchannel pool that is owned by a single channel. It doesn't
|
||||
// support subchannel sharing with other channels by nature. Nor does it support
|
||||
// subchannel retention when a subchannel is not used. The only real purpose of
|
||||
// using this subchannel pool is to allow subchannel reuse within the channel
|
||||
// when an incoming resolver update contains some addresses for which the
|
||||
// channel has already created subchannels.
|
||||
// Thread-unsafe.
|
||||
class LocalSubchannelPool final : public SubchannelPoolInterface { |
||||
public: |
||||
LocalSubchannelPool(); |
||||
~LocalSubchannelPool() override; |
||||
|
||||
// Implements interface methods.
|
||||
// Thread-unsafe. Intended to be invoked within the client_channel combiner.
|
||||
grpc_subchannel* RegisterSubchannel(SubchannelKey* key, |
||||
grpc_subchannel* constructed) override; |
||||
void UnregisterSubchannel(SubchannelKey* key) override; |
||||
grpc_subchannel* FindSubchannel(SubchannelKey* key) override; |
||||
|
||||
private: |
||||
// The vtable for subchannel operations in an AVL tree.
|
||||
static const grpc_avl_vtable subchannel_avl_vtable_; |
||||
// A map from subchannel key to subchannel.
|
||||
grpc_avl subchannel_map_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LOCAL_SUBCHANNEL_POOL_H */ |
@ -0,0 +1,97 @@ |
||||
//
|
||||
//
|
||||
// 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/ext/filters/client_channel/subchannel_pool_interface.h" |
||||
|
||||
#include "src/core/lib/gpr/useful.h" |
||||
|
||||
// The subchannel pool to reuse subchannels.
|
||||
#define GRPC_ARG_SUBCHANNEL_POOL "grpc.subchannel_pool" |
||||
// The subchannel key ID that is only used in test to make each key unique.
|
||||
#define GRPC_ARG_SUBCHANNEL_KEY_TEST_ONLY_ID "grpc.subchannel_key_test_only_id" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TraceFlag grpc_subchannel_pool_trace(false, "subchannel_pool"); |
||||
|
||||
SubchannelKey::SubchannelKey(const grpc_channel_args* args) { |
||||
Init(args, grpc_channel_args_normalize); |
||||
} |
||||
|
||||
SubchannelKey::~SubchannelKey() { |
||||
grpc_channel_args_destroy(const_cast<grpc_channel_args*>(args_)); |
||||
} |
||||
|
||||
SubchannelKey::SubchannelKey(const SubchannelKey& other) { |
||||
Init(other.args_, grpc_channel_args_copy); |
||||
} |
||||
|
||||
SubchannelKey& SubchannelKey::operator=(const SubchannelKey& other) { |
||||
grpc_channel_args_destroy(const_cast<grpc_channel_args*>(args_)); |
||||
Init(other.args_, grpc_channel_args_copy); |
||||
return *this; |
||||
} |
||||
|
||||
int SubchannelKey::Cmp(const SubchannelKey& other) const { |
||||
return grpc_channel_args_compare(args_, other.args_); |
||||
} |
||||
|
||||
void SubchannelKey::Init( |
||||
const grpc_channel_args* args, |
||||
grpc_channel_args* (*copy_channel_args)(const grpc_channel_args* args)) { |
||||
args_ = copy_channel_args(args); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
void* arg_copy(void* p) { |
||||
auto* subchannel_pool = static_cast<SubchannelPoolInterface*>(p); |
||||
subchannel_pool->Ref().release(); |
||||
return p; |
||||
} |
||||
|
||||
void arg_destroy(void* p) { |
||||
auto* subchannel_pool = static_cast<SubchannelPoolInterface*>(p); |
||||
subchannel_pool->Unref(); |
||||
} |
||||
|
||||
int arg_cmp(void* a, void* b) { return GPR_ICMP(a, b); } |
||||
|
||||
const grpc_arg_pointer_vtable subchannel_pool_arg_vtable = { |
||||
arg_copy, arg_destroy, arg_cmp}; |
||||
|
||||
} // namespace
|
||||
|
||||
grpc_arg SubchannelPoolInterface::CreateChannelArg( |
||||
SubchannelPoolInterface* subchannel_pool) { |
||||
return grpc_channel_arg_pointer_create( |
||||
const_cast<char*>(GRPC_ARG_SUBCHANNEL_POOL), subchannel_pool, |
||||
&subchannel_pool_arg_vtable); |
||||
} |
||||
|
||||
SubchannelPoolInterface* |
||||
SubchannelPoolInterface::GetSubchannelPoolFromChannelArgs( |
||||
const grpc_channel_args* args) { |
||||
const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_POOL); |
||||
if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return nullptr; |
||||
return static_cast<SubchannelPoolInterface*>(arg->value.pointer.p); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,94 @@ |
||||
/*
|
||||
* |
||||
* 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_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_POOL_INTERFACE_H |
||||
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_POOL_INTERFACE_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/avl/avl.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/gprpp/abstract.h" |
||||
#include "src/core/lib/gprpp/ref_counted.h" |
||||
|
||||
struct grpc_subchannel; |
||||
|
||||
namespace grpc_core { |
||||
|
||||
extern TraceFlag grpc_subchannel_pool_trace; |
||||
|
||||
// A key that can uniquely identify a subchannel.
|
||||
class SubchannelKey { |
||||
public: |
||||
explicit SubchannelKey(const grpc_channel_args* args); |
||||
~SubchannelKey(); |
||||
|
||||
// Copyable.
|
||||
SubchannelKey(const SubchannelKey& other); |
||||
SubchannelKey& operator=(const SubchannelKey& other); |
||||
// Not movable.
|
||||
SubchannelKey(SubchannelKey&&) = delete; |
||||
SubchannelKey& operator=(SubchannelKey&&) = delete; |
||||
|
||||
int Cmp(const SubchannelKey& other) const; |
||||
|
||||
private: |
||||
// Initializes the subchannel key with the given \a args and the function to
|
||||
// copy channel args.
|
||||
void Init( |
||||
const grpc_channel_args* args, |
||||
grpc_channel_args* (*copy_channel_args)(const grpc_channel_args* args)); |
||||
|
||||
const grpc_channel_args* args_; |
||||
}; |
||||
|
||||
// Interface for subchannel pool.
|
||||
// TODO(juanlishen): This refcounting mechanism may lead to memory leak.
|
||||
// To solve that, we should force polling to flush any pending callbacks, then
|
||||
// shut down safely. See https://github.com/grpc/grpc/issues/12560.
|
||||
class SubchannelPoolInterface : public RefCounted<SubchannelPoolInterface> { |
||||
public: |
||||
SubchannelPoolInterface() : RefCounted(&grpc_subchannel_pool_trace) {} |
||||
virtual ~SubchannelPoolInterface() {} |
||||
|
||||
// Registers a subchannel against a key. Returns the subchannel registered
|
||||
// with \a key, which may be different from \a constructed because we reuse
|
||||
// (instead of update) any existing subchannel already registered with \a key.
|
||||
virtual grpc_subchannel* RegisterSubchannel( |
||||
SubchannelKey* key, grpc_subchannel* constructed) GRPC_ABSTRACT; |
||||
|
||||
// Removes the registered subchannel found by \a key.
|
||||
virtual void UnregisterSubchannel(SubchannelKey* key) GRPC_ABSTRACT; |
||||
|
||||
// Finds the subchannel registered for the given subchannel key. Returns NULL
|
||||
// if no such channel exists. Thread-safe.
|
||||
virtual grpc_subchannel* FindSubchannel(SubchannelKey* key) GRPC_ABSTRACT; |
||||
|
||||
// Creates a channel arg from \a subchannel pool.
|
||||
static grpc_arg CreateChannelArg(SubchannelPoolInterface* subchannel_pool); |
||||
|
||||
// Gets the subchannel pool from the channel args.
|
||||
static SubchannelPoolInterface* GetSubchannelPoolFromChannelArgs( |
||||
const grpc_channel_args* args); |
||||
|
||||
GRPC_ABSTRACT_BASE_CLASS |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_POOL_INTERFACE_H */ |
@ -0,0 +1,47 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2019 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_OPTIONAL_H |
||||
#define GRPC_CORE_LIB_GPRPP_OPTIONAL_H |
||||
|
||||
namespace grpc_core { |
||||
|
||||
/* A make-shift alternative for absl::Optional. This can be removed in favor of
|
||||
* that once absl dependencies can be introduced. */ |
||||
template <typename T> |
||||
class Optional { |
||||
public: |
||||
void set(const T& val) { |
||||
value_ = val; |
||||
set_ = true; |
||||
} |
||||
|
||||
bool has_value() { return set_; } |
||||
|
||||
void reset() { set_ = false; } |
||||
|
||||
T value() { return value_; } |
||||
|
||||
private: |
||||
T value_; |
||||
bool set_ = false; |
||||
}; |
||||
|
||||
} /* namespace grpc_core */ |
||||
|
||||
#endif /* GRPC_CORE_LIB_GPRPP_OPTIONAL_H */ |
@ -1,36 +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/endpoint.h" |
||||
#include "src/core/lib/iomgr/network_status_tracker.h" |
||||
|
||||
void grpc_network_status_shutdown(void) {} |
||||
|
||||
void grpc_network_status_init(void) { |
||||
// TODO(makarandd): Install callback with OS to monitor network status.
|
||||
} |
||||
|
||||
void grpc_destroy_network_status_monitor() {} |
||||
|
||||
void grpc_network_status_register_endpoint(grpc_endpoint* ep) { (void)ep; } |
||||
|
||||
void grpc_network_status_unregister_endpoint(grpc_endpoint* ep) { (void)ep; } |
||||
|
||||
void grpc_network_status_shutdown_all_endpoints() {} |
@ -0,0 +1,110 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2019 The 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. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
using Grpc.Core.Internal; |
||||
|
||||
namespace Grpc.Core |
||||
{ |
||||
/// <summary> |
||||
/// Default implementation of <c>ServerCallContext</c>. |
||||
/// </summary> |
||||
internal class DefaultServerCallContext : ServerCallContext |
||||
{ |
||||
private readonly CallSafeHandle callHandle; |
||||
private readonly string method; |
||||
private readonly string host; |
||||
private readonly DateTime deadline; |
||||
private readonly Metadata requestHeaders; |
||||
private readonly CancellationToken cancellationToken; |
||||
private readonly Metadata responseTrailers; |
||||
private Status status; |
||||
private readonly IServerResponseStream serverResponseStream; |
||||
private readonly Lazy<AuthContext> authContext; |
||||
|
||||
/// <summary> |
||||
/// Creates a new instance of <c>ServerCallContext</c>. |
||||
/// To allow reuse of ServerCallContext API by different gRPC implementations, the implementation of some members is provided externally. |
||||
/// To provide state, this <c>ServerCallContext</c> instance and <c>extraData</c> will be passed to the member implementations. |
||||
/// </summary> |
||||
internal DefaultServerCallContext(CallSafeHandle callHandle, string method, string host, DateTime deadline, |
||||
Metadata requestHeaders, CancellationToken cancellationToken, IServerResponseStream serverResponseStream) |
||||
{ |
||||
this.callHandle = callHandle; |
||||
this.method = method; |
||||
this.host = host; |
||||
this.deadline = deadline; |
||||
this.requestHeaders = requestHeaders; |
||||
this.cancellationToken = cancellationToken; |
||||
this.responseTrailers = new Metadata(); |
||||
this.status = Status.DefaultSuccess; |
||||
this.serverResponseStream = serverResponseStream; |
||||
// TODO(jtattermusch): avoid unnecessary allocation of factory function and the lazy object |
||||
this.authContext = new Lazy<AuthContext>(GetAuthContextEager); |
||||
} |
||||
|
||||
protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions options) |
||||
{ |
||||
return new ContextPropagationToken(callHandle, deadline, cancellationToken, options); |
||||
} |
||||
|
||||
protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders) |
||||
{ |
||||
return serverResponseStream.WriteResponseHeadersAsync(responseHeaders); |
||||
} |
||||
|
||||
protected override string MethodCore => method; |
||||
|
||||
protected override string HostCore => host; |
||||
|
||||
protected override string PeerCore => callHandle.GetPeer(); |
||||
|
||||
protected override DateTime DeadlineCore => deadline; |
||||
|
||||
protected override Metadata RequestHeadersCore => requestHeaders; |
||||
|
||||
protected override CancellationToken CancellationTokenCore => cancellationToken; |
||||
|
||||
protected override Metadata ResponseTrailersCore => responseTrailers; |
||||
|
||||
protected override Status StatusCore |
||||
{ |
||||
get => status; |
||||
set => status = value; |
||||
} |
||||
|
||||
protected override WriteOptions WriteOptionsCore |
||||
{ |
||||
get => serverResponseStream.WriteOptions; |
||||
set => serverResponseStream.WriteOptions = value; |
||||
} |
||||
|
||||
protected override AuthContext AuthContextCore => authContext.Value; |
||||
|
||||
private AuthContext GetAuthContextEager() |
||||
{ |
||||
using (var authContextNative = callHandle.GetAuthContext()) |
||||
{ |
||||
return authContextNative.ToAuthContext(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,38 @@ |
||||
#region Copyright notice and license |
||||
// Copyright 2019 The 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. |
||||
#endregion |
||||
|
||||
using System; |
||||
using System.Threading.Tasks; |
||||
using Grpc.Core.Internal; |
||||
|
||||
namespace Grpc.Core.Internal |
||||
{ |
||||
/// <summary> |
||||
/// Exposes non-generic members of <c>ServerReponseStream</c>. |
||||
/// </summary> |
||||
internal interface IServerResponseStream |
||||
{ |
||||
/// <summary> |
||||
/// Asynchronously sends response headers for the current call to the client. See <c>ServerCallContext.WriteResponseHeadersAsync</c> for exact semantics. |
||||
/// </summary> |
||||
Task WriteResponseHeadersAsync(Metadata responseHeaders); |
||||
|
||||
/// <summary> |
||||
/// Gets or sets the write options. |
||||
/// </summary> |
||||
WriteOptions WriteOptions { get; set; } |
||||
} |
||||
} |
@ -0,0 +1 @@ |
||||
../../../LICENSE |
@ -0,0 +1 @@ |
||||
../../../LICENSE |
@ -0,0 +1 @@ |
||||
../../../LICENSE |
@ -0,0 +1 @@ |
||||
../../../LICENSE |
@ -1,39 +0,0 @@ |
||||
# Copyright 2018 The 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. |
||||
"""Provides distutils command classes for the GRPC Python setup process.""" |
||||
|
||||
import os |
||||
import shutil |
||||
|
||||
import setuptools |
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) |
||||
LICENSE = os.path.join(ROOT_DIR, '../../../LICENSE') |
||||
|
||||
|
||||
class Preprocess(setuptools.Command): |
||||
"""Command to copy LICENSE from root directory.""" |
||||
|
||||
description = '' |
||||
user_options = [] |
||||
|
||||
def initialize_options(self): |
||||
pass |
||||
|
||||
def finalize_options(self): |
||||
pass |
||||
|
||||
def run(self): |
||||
if os.path.isfile(LICENSE): |
||||
shutil.copyfile(LICENSE, os.path.join(ROOT_DIR, 'LICENSE')) |
@ -0,0 +1 @@ |
||||
../../../LICENSE |
@ -1,39 +0,0 @@ |
||||
# 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. |
||||
"""Provides distutils command classes for the GRPC Python setup process.""" |
||||
|
||||
import os |
||||
import shutil |
||||
|
||||
import setuptools |
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) |
||||
LICENSE = os.path.join(ROOT_DIR, '../../../LICENSE') |
||||
|
||||
|
||||
class Preprocess(setuptools.Command): |
||||
"""Command to copy LICENSE from root directory.""" |
||||
|
||||
description = '' |
||||
user_options = [] |
||||
|
||||
def initialize_options(self): |
||||
pass |
||||
|
||||
def finalize_options(self): |
||||
pass |
||||
|
||||
def run(self): |
||||
if os.path.isfile(LICENSE): |
||||
shutil.copyfile(LICENSE, os.path.join(ROOT_DIR, 'LICENSE')) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue