commit
8e99f682e8
44 changed files with 810 additions and 208 deletions
@ -0,0 +1,41 @@ |
||||
# gRPC Versioning Guide |
||||
|
||||
## Versioning Overview |
||||
|
||||
All gRPC implementations use a three-part version number (`vX.Y.Z`) and strictly follow [semantic versioning](https://semver.org/), which defines the semantics of major, minor and patch components of the version number. In addition to that, gRPC versions evolve according to these rules: |
||||
- **Major version bumps** only happen on rare occasions. In order to qualify for a major version bump, certain criteria described later in this document need to be met. Most importantly, a major version increase must not break wire compatibility with other gRPC implementations so that existing gRPC libraries remain fully interoperable. |
||||
- **Minor version bumps** happen approx. every 6 weeks as part of the normal release cycle as defined by the gRPC release process. A new release branch named vMAJOR.MINOR.PATCH) is cut every 6 weeks based on the [release schedule](https://github.com/grpc/grpc/blob/master/doc/grpc_release_schedule.md). |
||||
- **Patch version bump** corresponds to bugfixes done on release branch. |
||||
|
||||
There are also a few extra rules regarding adding new gRPC implementations (e.g. adding support for a new language) |
||||
- New implementations start at v0.x.y version and until they reach 1.0, they are considered not ready for production workloads. Breaking API changes are allowed in the 0.x releases as the library is not considered stable yet. |
||||
- The "1.0" release has semantics of GA (generally available) and being production ready. Requirements to reach this milestone are at least these |
||||
- basic RPC features are feature complete and tested |
||||
- implementation is tested for interoperability with other languages |
||||
- Public API is declared stable |
||||
- Once a gRPC library reaches 1.0 (or higher version), the normal rules for versioning apply. |
||||
|
||||
## Policy for updating the major version number |
||||
|
||||
To avoid user confusion and simplify reasoning, the gRPC releases in different languages try to stay synchronized in terms of major and minor version (all languages follow the same release schedule). Nevertheless, because we also strictly follow semantic versioning, there are circumstances in which a gRPC implementation needs to break the version synchronicity and do a major version bump independently of other languages. |
||||
|
||||
### Situations when it's ok to do a major version bump |
||||
- **change forced by the language ecosystem:** when the language itself or its standard libraries that we depend on make a breaking change (something which is out of our control), reacting with updating gRPC APIs may be the only adequate response. |
||||
- **voluntary change:** Even in non-forced situations, there might be circumstances in which a breaking API change makes sense and represents a net win, but as a rule of thumb breaking changes are very disruptive for users, cause user fragmentation and incur high maintenance costs. Therefore, breaking API changes should be very rare events that need to be considered with extreme care and the bar for accepting such changes is intentionally set very high. |
||||
Example scenarios where a breaking API change might be adequate: |
||||
- fixing a security problem which requires changes to API (need to consider the non-breaking alternatives first) |
||||
- the change leads to very significant gains to security, usability or development velocity. These gains need to be clearly documented and claims need to be supported by evidence (ideally by numbers). Costs to the ecosystem (impact on users, dev team etc.) need to be taken into account and the change still needs to be a net positive after subtracting the costs. |
||||
|
||||
All proposals to make a breaking change need to be documented as a gRFC document (in the grpc/proposal repository) that covers at least these areas: |
||||
- Description of the proposal including an explanation why the proposed change is one of the very rare events where a breaking change is introduced. |
||||
- Migration costs (= what does it mean for the users to migrate to the new API, what are the costs and risks associated with it) |
||||
- Pros of the change (what is gained and how) |
||||
- Cons of the change (e.g. user confusion, lost users and user trust, work needed, added maintenance costs) |
||||
- Plan for supporting users still using the old major version (in case migration to the new major version is not trivial or not everyone can migrate easily) |
||||
|
||||
Note that while major version bump allows changing APIs used by the users, it must not impact the interoperability of the implementation with other gRPC implementations and the previous major version released. That means that **no backward incompatible protocol changes are allowed**: old clients must continue interoperating correctly with new servers and new servers with old clients. |
||||
|
||||
### Situations that DON'T warrant a major version bump |
||||
- Because other languages do so. This is not a good enough reason because |
||||
doing a major version bump has high potential for disturbing and confusing the users of that language and fragmenting the user base and that is a bigger threat than having language implementations at different major version (provided the state is well documented). Having some languages at different major version seems to be unavoidable anyway (due to forced version bumps), unless we bump some languages artificially. |
||||
- "I don't like this API": In retrospect, some API decisions made in the past necessarily turn out more lucky than others, but without strong reasons that would be in favor of changing the API and without enough supporting evidence (see previous section), other strategy than making a breaking API change needs to be used. Possible options: Expand the API to make it useful again; mark API as deprecated while keeping its functionality and providing a new better API. |
@ -0,0 +1,138 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/iomgr/executor/threadpool.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
void ThreadPoolWorker::Run() { |
||||
while (true) { |
||||
void* elem; |
||||
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_thread_pool_trace)) { |
||||
// Updates stats and print
|
||||
gpr_timespec wait_time = gpr_time_0(GPR_TIMESPAN); |
||||
elem = queue_->Get(&wait_time); |
||||
stats_.sleep_time = gpr_time_add(stats_.sleep_time, wait_time); |
||||
gpr_log(GPR_INFO, |
||||
"ThreadPool Worker [%s %d] Stats: sleep_time %f", |
||||
thd_name_, index_, gpr_timespec_to_micros(stats_.sleep_time)); |
||||
} else { |
||||
elem = queue_->Get(nullptr); |
||||
} |
||||
if (elem == nullptr) { |
||||
break; |
||||
} |
||||
// Runs closure
|
||||
auto* closure = |
||||
static_cast<grpc_experimental_completion_queue_functor*>(elem); |
||||
closure->functor_run(closure, closure->internal_success); |
||||
} |
||||
} |
||||
|
||||
void ThreadPool::SharedThreadPoolConstructor() { |
||||
// All worker threads in thread pool must be joinable.
|
||||
thread_options_.set_joinable(true); |
||||
|
||||
// Create at least 1 worker thread.
|
||||
if (num_threads_ <= 0) num_threads_ = 1; |
||||
|
||||
queue_ = New<InfLenFIFOQueue>(); |
||||
threads_ = static_cast<ThreadPoolWorker**>( |
||||
gpr_zalloc(num_threads_ * sizeof(ThreadPoolWorker*))); |
||||
for (int i = 0; i < num_threads_; ++i) { |
||||
threads_[i] = |
||||
New<ThreadPoolWorker>(thd_name_, this, queue_, thread_options_, i); |
||||
threads_[i]->Start(); |
||||
} |
||||
} |
||||
|
||||
size_t ThreadPool::DefaultStackSize() { |
||||
#if defined(__ANDROID__) || defined(__APPLE__) |
||||
return 1952 * 1024; |
||||
#else |
||||
return 64 * 1024; |
||||
#endif |
||||
} |
||||
|
||||
void ThreadPool::AssertHasNotBeenShutDown() { |
||||
// For debug checking purpose, using RELAXED order is sufficient.
|
||||
GPR_DEBUG_ASSERT(!shut_down_.Load(MemoryOrder::RELAXED)); |
||||
} |
||||
|
||||
ThreadPool::ThreadPool(int num_threads) : num_threads_(num_threads) { |
||||
thd_name_ = "ThreadPoolWorker"; |
||||
thread_options_ = Thread::Options(); |
||||
thread_options_.set_stack_size(DefaultStackSize()); |
||||
SharedThreadPoolConstructor(); |
||||
} |
||||
|
||||
ThreadPool::ThreadPool(int num_threads, const char* thd_name) |
||||
: num_threads_(num_threads), thd_name_(thd_name) { |
||||
thread_options_ = Thread::Options(); |
||||
thread_options_.set_stack_size(DefaultStackSize()); |
||||
SharedThreadPoolConstructor(); |
||||
} |
||||
|
||||
ThreadPool::ThreadPool(int num_threads, const char* thd_name, |
||||
const Thread::Options& thread_options) |
||||
: num_threads_(num_threads), |
||||
thd_name_(thd_name), |
||||
thread_options_(thread_options) { |
||||
if (thread_options_.stack_size() == 0) { |
||||
thread_options_.set_stack_size(DefaultStackSize()); |
||||
} |
||||
SharedThreadPoolConstructor(); |
||||
} |
||||
|
||||
ThreadPool::~ThreadPool() { |
||||
// For debug checking purpose, using RELAXED order is sufficient.
|
||||
shut_down_.Store(true, MemoryOrder::RELAXED); |
||||
|
||||
for (int i = 0; i < num_threads_; ++i) { |
||||
queue_->Put(nullptr); |
||||
} |
||||
|
||||
for (int i = 0; i < num_threads_; ++i) { |
||||
threads_[i]->Join(); |
||||
} |
||||
|
||||
for (int i = 0; i < num_threads_; ++i) { |
||||
Delete(threads_[i]); |
||||
} |
||||
gpr_free(threads_); |
||||
Delete(queue_); |
||||
} |
||||
|
||||
void ThreadPool::Add(grpc_experimental_completion_queue_functor* closure) { |
||||
AssertHasNotBeenShutDown(); |
||||
queue_->Put(static_cast<void*>(closure)); |
||||
} |
||||
|
||||
int ThreadPool::num_pending_closures() const { return queue_->count(); } |
||||
|
||||
int ThreadPool::pool_capacity() const { return num_threads_; } |
||||
|
||||
const Thread::Options& ThreadPool::thread_options() const { |
||||
return thread_options_; |
||||
} |
||||
|
||||
const char* ThreadPool::thread_name() const { return thd_name_; } |
||||
} // namespace grpc_core
|
@ -0,0 +1,153 @@ |
||||
/*
|
||||
* |
||||
* 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_IOMGR_EXECUTOR_THREADPOOL_H |
||||
#define GRPC_CORE_LIB_IOMGR_EXECUTOR_THREADPOOL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include "src/core/lib/gprpp/thd.h" |
||||
#include "src/core/lib/iomgr/executor/mpmcqueue.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// A base abstract base class for threadpool.
|
||||
// Threadpool is an executor that maintains a pool of threads sitting around
|
||||
// and waiting for closures. A threadpool also maintains a queue of pending
|
||||
// closures, when closures appearing in the queue, the threads in pool will
|
||||
// pull them out and execute them.
|
||||
class ThreadPoolInterface { |
||||
public: |
||||
// Waits for all pending closures to complete, then shuts down thread pool.
|
||||
virtual ~ThreadPoolInterface() {} |
||||
|
||||
// Schedules a given closure for execution later.
|
||||
// Depending on specific subclass implementation, this routine might cause
|
||||
// current thread to be blocked (in case of unable to schedule).
|
||||
// Closure should contain a function pointer and arguments it will take, more
|
||||
// details for closure struct at /grpc/include/grpc/impl/codegen/grpc_types.h
|
||||
virtual void Add(grpc_experimental_completion_queue_functor* closure) |
||||
GRPC_ABSTRACT; |
||||
|
||||
// Returns the current number of pending closures
|
||||
virtual int num_pending_closures() const GRPC_ABSTRACT; |
||||
|
||||
// Returns the capacity of pool (number of worker threads in pool)
|
||||
virtual int pool_capacity() const GRPC_ABSTRACT; |
||||
|
||||
// Thread option accessor
|
||||
virtual const Thread::Options& thread_options() const GRPC_ABSTRACT; |
||||
|
||||
// Returns the thread name for threads in this ThreadPool.
|
||||
virtual const char* thread_name() const GRPC_ABSTRACT; |
||||
|
||||
GRPC_ABSTRACT_BASE_CLASS |
||||
}; |
||||
|
||||
// Worker thread for threadpool. Executes closures in the queue, until getting a
|
||||
// NULL closure.
|
||||
class ThreadPoolWorker { |
||||
public: |
||||
ThreadPoolWorker(const char* thd_name, ThreadPoolInterface* pool, |
||||
MPMCQueueInterface* queue, Thread::Options& options, |
||||
int index) |
||||
: queue_(queue), thd_name_(thd_name), index_(index) { |
||||
thd_ = Thread(thd_name, |
||||
[](void* th) { static_cast<ThreadPoolWorker*>(th)->Run(); }, |
||||
this, nullptr, options); |
||||
} |
||||
|
||||
~ThreadPoolWorker() {} |
||||
|
||||
void Start() { thd_.Start(); } |
||||
void Join() { thd_.Join(); } |
||||
|
||||
private: |
||||
// struct for tracking stats of thread
|
||||
struct Stats { |
||||
gpr_timespec sleep_time; |
||||
Stats() { sleep_time = gpr_time_0(GPR_TIMESPAN); } |
||||
}; |
||||
|
||||
void Run(); // Pulls closures from queue and executes them
|
||||
|
||||
MPMCQueueInterface* queue_; // Queue in thread pool to pull closures from
|
||||
Thread thd_; // Thread wrapped in
|
||||
Stats stats_; // Stats to be collected in run time
|
||||
const char* thd_name_; // Name of thread
|
||||
int index_; // Index in thread pool
|
||||
}; |
||||
|
||||
// A fixed size thread pool implementation of abstract thread pool interface.
|
||||
// In this implementation, the number of threads in pool is fixed, but the
|
||||
// capacity of closure queue is unlimited.
|
||||
class ThreadPool : public ThreadPoolInterface { |
||||
public: |
||||
// Creates a thread pool with size of "num_threads", with default thread name
|
||||
// "ThreadPoolWorker" and all thread options set to default. If the given size
|
||||
// is 0 or less, there will be 1 worker thread created inside pool.
|
||||
ThreadPool(int num_threads); |
||||
|
||||
// Same as ThreadPool(int num_threads) constructor, except
|
||||
// that it also sets "thd_name" as the name of all threads in the thread pool.
|
||||
ThreadPool(int num_threads, const char* thd_name); |
||||
|
||||
// Same as ThreadPool(const char *thd_name, int num_threads) constructor,
|
||||
// except that is also set thread_options for threads.
|
||||
// Notes for stack size:
|
||||
// If the stack size field of the passed in Thread::Options is set to default
|
||||
// value 0, default ThreadPool stack size will be used. The current default
|
||||
// stack size of this implementation is 1952K for mobile platform and 64K for
|
||||
// all others.
|
||||
ThreadPool(int num_threads, const char* thd_name, |
||||
const Thread::Options& thread_options); |
||||
|
||||
// Waits for all pending closures to complete, then shuts down thread pool.
|
||||
~ThreadPool() override; |
||||
|
||||
// Adds given closure into pending queue immediately. Since closure queue has
|
||||
// infinite length, this routine will not block.
|
||||
void Add(grpc_experimental_completion_queue_functor* closure) override; |
||||
|
||||
int num_pending_closures() const override; |
||||
int pool_capacity() const override; |
||||
const Thread::Options& thread_options() const override; |
||||
const char* thread_name() const override; |
||||
|
||||
private: |
||||
int num_threads_ = 0; |
||||
const char* thd_name_ = nullptr; |
||||
Thread::Options thread_options_; |
||||
ThreadPoolWorker** threads_ = nullptr; // Array of worker threads
|
||||
MPMCQueueInterface* queue_ = nullptr; // Closure queue
|
||||
|
||||
Atomic<bool> shut_down_{false}; // Destructor has been called if set to true
|
||||
|
||||
void SharedThreadPoolConstructor(); |
||||
// For ThreadPool, default stack size for mobile platform is 1952K. for other
|
||||
// platforms is 64K.
|
||||
size_t DefaultStackSize(); |
||||
// Internal Use Only for debug checking.
|
||||
void AssertHasNotBeenShutDown(); |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_THREADPOOL_H */ |
@ -1,47 +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. |
||||
* |
||||
*/ |
||||
|
||||
#import <Foundation/Foundation.h> |
||||
#import <SystemConfiguration/SystemConfiguration.h> |
||||
|
||||
typedef NS_ENUM(NSInteger, GRPCConnectivityStatus) { |
||||
GRPCConnectivityUnknown = 0, |
||||
GRPCConnectivityNoNetwork = 1, |
||||
GRPCConnectivityCellular = 2, |
||||
GRPCConnectivityWiFi = 3, |
||||
}; |
||||
|
||||
extern NSString* _Nonnull kGRPCConnectivityNotification; |
||||
|
||||
// This interface monitors OS reachability interface for any network status
|
||||
// change. Parties interested in these events should register themselves as
|
||||
// observer.
|
||||
@interface GRPCConnectivityMonitor : NSObject |
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE; |
||||
|
||||
// Register an object as observer of network status change. \a observer
|
||||
// must have a notification method with one parameter of type
|
||||
// (NSNotification *) and should pass it to parameter \a selector. The
|
||||
// parameter of this notification method is not used for now.
|
||||
+ (void)registerObserver:(_Nonnull id)observer selector:(_Nonnull SEL)selector; |
||||
|
||||
// Ungegister an object from observers of network status change.
|
||||
+ (void)unregisterObserver:(_Nonnull id)observer; |
||||
|
||||
@end |
@ -1,90 +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. |
||||
* |
||||
*/ |
||||
|
||||
#import "GRPCConnectivityMonitor.h" |
||||
|
||||
#include <netinet/in.h> |
||||
|
||||
NSString *kGRPCConnectivityNotification = @"kGRPCConnectivityNotification"; |
||||
|
||||
static SCNetworkReachabilityRef reachability; |
||||
static GRPCConnectivityStatus currentStatus; |
||||
|
||||
// Aggregate information in flags into network status. |
||||
GRPCConnectivityStatus CalculateConnectivityStatus(SCNetworkReachabilityFlags flags) { |
||||
GRPCConnectivityStatus result = GRPCConnectivityUnknown; |
||||
if (((flags & kSCNetworkReachabilityFlagsReachable) == 0) || |
||||
((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0)) { |
||||
return GRPCConnectivityNoNetwork; |
||||
} |
||||
result = GRPCConnectivityWiFi; |
||||
#if TARGET_OS_IPHONE |
||||
if (flags & kSCNetworkReachabilityFlagsIsWWAN) { |
||||
return result = GRPCConnectivityCellular; |
||||
} |
||||
#endif |
||||
return result; |
||||
} |
||||
|
||||
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, |
||||
void *info) { |
||||
GRPCConnectivityStatus newStatus = CalculateConnectivityStatus(flags); |
||||
|
||||
if (newStatus != currentStatus) { |
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kGRPCConnectivityNotification |
||||
object:nil]; |
||||
currentStatus = newStatus; |
||||
} |
||||
} |
||||
|
||||
@implementation GRPCConnectivityMonitor |
||||
|
||||
+ (void)initialize { |
||||
if (self == [GRPCConnectivityMonitor self]) { |
||||
struct sockaddr_in addr = {0}; |
||||
addr.sin_len = sizeof(addr); |
||||
addr.sin_family = AF_INET; |
||||
reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&addr); |
||||
currentStatus = GRPCConnectivityUnknown; |
||||
|
||||
SCNetworkConnectionFlags flags; |
||||
if (SCNetworkReachabilityGetFlags(reachability, &flags)) { |
||||
currentStatus = CalculateConnectivityStatus(flags); |
||||
} |
||||
|
||||
SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; |
||||
if (!SCNetworkReachabilitySetCallback(reachability, ReachabilityCallback, &context) || |
||||
!SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), |
||||
kCFRunLoopCommonModes)) { |
||||
NSLog(@"gRPC connectivity monitor fail to set"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
+ (void)registerObserver:(id)observer selector:(SEL)selector { |
||||
[[NSNotificationCenter defaultCenter] addObserver:observer |
||||
selector:selector |
||||
name:kGRPCConnectivityNotification |
||||
object:nil]; |
||||
} |
||||
|
||||
+ (void)unregisterObserver:(id)observer { |
||||
[[NSNotificationCenter defaultCenter] removeObserver:observer]; |
||||
} |
||||
|
||||
@end |
@ -0,0 +1,192 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/lib/iomgr/executor/threadpool.h" |
||||
|
||||
#include "test/core/util/test_config.h" |
||||
|
||||
static const int kSmallThreadPoolSize = 20; |
||||
static const int kLargeThreadPoolSize = 100; |
||||
static const int kThreadSmallIter = 100; |
||||
static const int kThreadLargeIter = 10000; |
||||
|
||||
static void test_size_zero(void) { |
||||
gpr_log(GPR_INFO, "test_size_zero"); |
||||
grpc_core::ThreadPool* pool_size_zero = |
||||
grpc_core::New<grpc_core::ThreadPool>(0); |
||||
GPR_ASSERT(pool_size_zero->pool_capacity() == 1); |
||||
Delete(pool_size_zero); |
||||
} |
||||
|
||||
static void test_constructor_option(void) { |
||||
gpr_log(GPR_INFO, "test_constructor_option"); |
||||
// Tests options
|
||||
grpc_core::Thread::Options options; |
||||
options.set_stack_size(192 * 1024); // Random non-default value
|
||||
grpc_core::ThreadPool* pool = grpc_core::New<grpc_core::ThreadPool>( |
||||
0, "test_constructor_option", options); |
||||
GPR_ASSERT(pool->thread_options().stack_size() == options.stack_size()); |
||||
Delete(pool); |
||||
} |
||||
|
||||
// Simple functor for testing. It will count how many times being called.
|
||||
class SimpleFunctorForAdd : public grpc_experimental_completion_queue_functor { |
||||
public: |
||||
friend class SimpleFunctorCheckForAdd; |
||||
SimpleFunctorForAdd() { |
||||
functor_run = &SimpleFunctorForAdd::Run; |
||||
internal_next = this; |
||||
internal_success = 0; |
||||
} |
||||
~SimpleFunctorForAdd() {} |
||||
static void Run(struct grpc_experimental_completion_queue_functor* cb, |
||||
int ok) { |
||||
auto* callback = static_cast<SimpleFunctorForAdd*>(cb); |
||||
callback->count_.FetchAdd(1, grpc_core::MemoryOrder::RELAXED); |
||||
} |
||||
|
||||
int count() { return count_.Load(grpc_core::MemoryOrder::RELAXED); } |
||||
|
||||
private: |
||||
grpc_core::Atomic<int> count_{0}; |
||||
}; |
||||
|
||||
static void test_add(void) { |
||||
gpr_log(GPR_INFO, "test_add"); |
||||
grpc_core::ThreadPool* pool = |
||||
grpc_core::New<grpc_core::ThreadPool>(kSmallThreadPoolSize, "test_add"); |
||||
|
||||
SimpleFunctorForAdd* functor = grpc_core::New<SimpleFunctorForAdd>(); |
||||
for (int i = 0; i < kThreadSmallIter; ++i) { |
||||
pool->Add(functor); |
||||
} |
||||
grpc_core::Delete(pool); |
||||
GPR_ASSERT(functor->count() == kThreadSmallIter); |
||||
grpc_core::Delete(functor); |
||||
gpr_log(GPR_DEBUG, "Done."); |
||||
} |
||||
|
||||
// Thread that adds closures to pool
|
||||
class WorkThread { |
||||
public: |
||||
WorkThread(grpc_core::ThreadPool* pool, SimpleFunctorForAdd* cb, int num_add) |
||||
: num_add_(num_add), cb_(cb), pool_(pool) { |
||||
thd_ = grpc_core::Thread( |
||||
"thread_pool_test_add_thd", |
||||
[](void* th) { static_cast<WorkThread*>(th)->Run(); }, this); |
||||
} |
||||
~WorkThread() {} |
||||
|
||||
void Start() { thd_.Start(); } |
||||
void Join() { thd_.Join(); } |
||||
|
||||
private: |
||||
void Run() { |
||||
for (int i = 0; i < num_add_; ++i) { |
||||
pool_->Add(cb_); |
||||
} |
||||
} |
||||
|
||||
int num_add_; |
||||
SimpleFunctorForAdd* cb_; |
||||
grpc_core::ThreadPool* pool_; |
||||
grpc_core::Thread thd_; |
||||
}; |
||||
|
||||
static void test_multi_add(void) { |
||||
gpr_log(GPR_INFO, "test_multi_add"); |
||||
const int num_work_thds = 10; |
||||
grpc_core::ThreadPool* pool = grpc_core::New<grpc_core::ThreadPool>( |
||||
kLargeThreadPoolSize, "test_multi_add"); |
||||
SimpleFunctorForAdd* functor = grpc_core::New<SimpleFunctorForAdd>(); |
||||
WorkThread** work_thds = static_cast<WorkThread**>( |
||||
gpr_zalloc(sizeof(WorkThread*) * num_work_thds)); |
||||
gpr_log(GPR_DEBUG, "Fork threads for adding..."); |
||||
for (int i = 0; i < num_work_thds; ++i) { |
||||
work_thds[i] = grpc_core::New<WorkThread>(pool, functor, kThreadLargeIter); |
||||
work_thds[i]->Start(); |
||||
} |
||||
// Wait for all threads finish
|
||||
gpr_log(GPR_DEBUG, "Waiting for all work threads finish..."); |
||||
for (int i = 0; i < num_work_thds; ++i) { |
||||
work_thds[i]->Join(); |
||||
grpc_core::Delete(work_thds[i]); |
||||
} |
||||
gpr_free(work_thds); |
||||
gpr_log(GPR_DEBUG, "Done."); |
||||
gpr_log(GPR_DEBUG, "Waiting for all closures finish..."); |
||||
// Destructor of thread pool will wait for all closures to finish
|
||||
grpc_core::Delete(pool); |
||||
GPR_ASSERT(functor->count() == kThreadLargeIter * num_work_thds); |
||||
grpc_core::Delete(functor); |
||||
gpr_log(GPR_DEBUG, "Done."); |
||||
} |
||||
|
||||
// Checks the current count with a given number.
|
||||
class SimpleFunctorCheckForAdd |
||||
: public grpc_experimental_completion_queue_functor { |
||||
public: |
||||
SimpleFunctorCheckForAdd(int ok, int* count) : count_(count) { |
||||
functor_run = &SimpleFunctorCheckForAdd::Run; |
||||
internal_success = ok; |
||||
} |
||||
~SimpleFunctorCheckForAdd() {} |
||||
static void Run(struct grpc_experimental_completion_queue_functor* cb, |
||||
int ok) { |
||||
auto* callback = static_cast<SimpleFunctorCheckForAdd*>(cb); |
||||
(*callback->count_)++; |
||||
GPR_ASSERT(*callback->count_ == callback->internal_success); |
||||
} |
||||
|
||||
private: |
||||
int* count_; |
||||
}; |
||||
|
||||
static void test_one_thread_FIFO(void) { |
||||
gpr_log(GPR_INFO, "test_one_thread_FIFO"); |
||||
int counter = 0; |
||||
grpc_core::ThreadPool* pool = |
||||
grpc_core::New<grpc_core::ThreadPool>(1, "test_one_thread_FIFO"); |
||||
SimpleFunctorCheckForAdd** check_functors = |
||||
static_cast<SimpleFunctorCheckForAdd**>( |
||||
gpr_zalloc(sizeof(SimpleFunctorCheckForAdd*) * kThreadSmallIter)); |
||||
for (int i = 0; i < kThreadSmallIter; ++i) { |
||||
check_functors[i] = |
||||
grpc_core::New<SimpleFunctorCheckForAdd>(i + 1, &counter); |
||||
pool->Add(check_functors[i]); |
||||
} |
||||
// Destructor of pool will wait until all closures finished.
|
||||
grpc_core::Delete(pool); |
||||
for (int i = 0; i < kThreadSmallIter; ++i) { |
||||
grpc_core::Delete(check_functors[i]); |
||||
} |
||||
gpr_free(check_functors); |
||||
gpr_log(GPR_DEBUG, "Done."); |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc::testing::TestEnvironment env(argc, argv); |
||||
grpc_init(); |
||||
test_size_zero(); |
||||
test_constructor_option(); |
||||
test_add(); |
||||
test_multi_add(); |
||||
test_one_thread_FIFO(); |
||||
grpc_shutdown(); |
||||
return 0; |
||||
} |
@ -0,0 +1,23 @@ |
||||
# 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. |
||||
|
||||
# Config file for the internal CI (in protobuf text format) |
||||
|
||||
# Location of the continuous shell script in repository. |
||||
build_file: "grpc/tools/internal_ci/macos/grpc_run_bazel_tests.sh" |
||||
env_vars { |
||||
key: "RUN_TESTS_FLAGS" |
||||
value: "--config=asan" |
||||
} |
||||
|
@ -0,0 +1,23 @@ |
||||
# 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. |
||||
|
||||
# Config file for the internal CI (in protobuf text format) |
||||
|
||||
# Location of the continuous shell script in repository. |
||||
build_file: "grpc/tools/internal_ci/macos/grpc_run_bazel_tests.sh" |
||||
env_vars { |
||||
key: "RUN_TESTS_FLAGS" |
||||
value: "--config=tsan" |
||||
} |
||||
|
Loading…
Reference in new issue