|
|
|
@ -45,20 +45,70 @@ |
|
|
|
|
gpr_log(GPR_INFO, "EXECUTOR " str); \
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_core::TraceFlag executor_trace(false, "executor"); |
|
|
|
|
namespace grpc_core { |
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
GPR_TLS_DECL(g_this_thread_state); |
|
|
|
|
|
|
|
|
|
GrpcExecutor::GrpcExecutor(const char* name) : name_(name) { |
|
|
|
|
Executor* executors[static_cast<size_t>(ExecutorType::NUM_EXECUTORS)]; |
|
|
|
|
|
|
|
|
|
void default_enqueue_short(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)]->Enqueue( |
|
|
|
|
closure, error, true /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void default_enqueue_long(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)]->Enqueue( |
|
|
|
|
closure, error, false /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void resolver_enqueue_short(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)]->Enqueue( |
|
|
|
|
closure, error, true /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void resolver_enqueue_long(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)]->Enqueue( |
|
|
|
|
closure, error, false /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const grpc_closure_scheduler_vtable |
|
|
|
|
vtables_[static_cast<size_t>(ExecutorType::NUM_EXECUTORS)] |
|
|
|
|
[static_cast<size_t>(ExecutorJobType::NUM_JOB_TYPES)] = { |
|
|
|
|
{{&default_enqueue_short, &default_enqueue_short, |
|
|
|
|
"def-ex-short"}, |
|
|
|
|
{&default_enqueue_long, &default_enqueue_long, "def-ex-long"}}, |
|
|
|
|
{{&resolver_enqueue_short, &resolver_enqueue_short, |
|
|
|
|
"res-ex-short"}, |
|
|
|
|
{&resolver_enqueue_long, &resolver_enqueue_long, |
|
|
|
|
"res-ex-long"}}}; |
|
|
|
|
|
|
|
|
|
grpc_closure_scheduler |
|
|
|
|
schedulers_[static_cast<size_t>(ExecutorType::NUM_EXECUTORS)] |
|
|
|
|
[static_cast<size_t>(ExecutorJobType::NUM_JOB_TYPES)] = { |
|
|
|
|
{{&vtables_[static_cast<size_t>(ExecutorType::DEFAULT)] |
|
|
|
|
[static_cast<size_t>(ExecutorJobType::SHORT)]}, |
|
|
|
|
{&vtables_[static_cast<size_t>(ExecutorType::DEFAULT)] |
|
|
|
|
[static_cast<size_t>(ExecutorJobType::LONG)]}}, |
|
|
|
|
{{&vtables_[static_cast<size_t>(ExecutorType::RESOLVER)] |
|
|
|
|
[static_cast<size_t>(ExecutorJobType::SHORT)]}, |
|
|
|
|
{&vtables_[static_cast<size_t>(ExecutorType::RESOLVER)] |
|
|
|
|
[static_cast<size_t>(ExecutorJobType::LONG)]}}}; |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
TraceFlag executor_trace(false, "executor"); |
|
|
|
|
|
|
|
|
|
Executor::Executor(const char* name) : name_(name) { |
|
|
|
|
adding_thread_lock_ = GPR_SPINLOCK_STATIC_INITIALIZER; |
|
|
|
|
gpr_atm_rel_store(&num_threads_, 0); |
|
|
|
|
max_threads_ = GPR_MAX(1, 2 * gpr_cpu_num_cores()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void GrpcExecutor::Init() { SetThreading(true); } |
|
|
|
|
void Executor::Init() { SetThreading(true); } |
|
|
|
|
|
|
|
|
|
size_t GrpcExecutor::RunClosures(const char* executor_name, |
|
|
|
|
grpc_closure_list list) { |
|
|
|
|
size_t Executor::RunClosures(const char* executor_name, |
|
|
|
|
grpc_closure_list list) { |
|
|
|
|
size_t n = 0; |
|
|
|
|
|
|
|
|
|
grpc_closure* c = list.head; |
|
|
|
@ -82,11 +132,11 @@ size_t GrpcExecutor::RunClosures(const char* executor_name, |
|
|
|
|
return n; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool GrpcExecutor::IsThreaded() const { |
|
|
|
|
bool Executor::IsThreaded() const { |
|
|
|
|
return gpr_atm_acq_load(&num_threads_) > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void GrpcExecutor::SetThreading(bool threading) { |
|
|
|
|
void Executor::SetThreading(bool threading) { |
|
|
|
|
gpr_atm curr_num_threads = gpr_atm_acq_load(&num_threads_); |
|
|
|
|
EXECUTOR_TRACE("(%s) SetThreading(%d) begin", name_, threading); |
|
|
|
|
|
|
|
|
@ -112,7 +162,7 @@ void GrpcExecutor::SetThreading(bool threading) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
thd_state_[0].thd = |
|
|
|
|
grpc_core::Thread(name_, &GrpcExecutor::ThreadMain, &thd_state_[0]); |
|
|
|
|
grpc_core::Thread(name_, &Executor::ThreadMain, &thd_state_[0]); |
|
|
|
|
thd_state_[0].thd.Start(); |
|
|
|
|
} else { // !threading
|
|
|
|
|
if (curr_num_threads == 0) { |
|
|
|
@ -153,9 +203,9 @@ void GrpcExecutor::SetThreading(bool threading) { |
|
|
|
|
EXECUTOR_TRACE("(%s) SetThreading(%d) done", name_, threading); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void GrpcExecutor::Shutdown() { SetThreading(false); } |
|
|
|
|
void Executor::Shutdown() { SetThreading(false); } |
|
|
|
|
|
|
|
|
|
void GrpcExecutor::ThreadMain(void* arg) { |
|
|
|
|
void Executor::ThreadMain(void* arg) { |
|
|
|
|
ThreadState* ts = static_cast<ThreadState*>(arg); |
|
|
|
|
gpr_tls_set(&g_this_thread_state, reinterpret_cast<intptr_t>(ts)); |
|
|
|
|
|
|
|
|
@ -192,8 +242,8 @@ void GrpcExecutor::ThreadMain(void* arg) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void GrpcExecutor::Enqueue(grpc_closure* closure, grpc_error* error, |
|
|
|
|
bool is_short) { |
|
|
|
|
void Executor::Enqueue(grpc_closure* closure, grpc_error* error, |
|
|
|
|
bool is_short) { |
|
|
|
|
bool retry_push; |
|
|
|
|
if (is_short) { |
|
|
|
|
GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS(); |
|
|
|
@ -304,7 +354,7 @@ void GrpcExecutor::Enqueue(grpc_closure* closure, grpc_error* error, |
|
|
|
|
gpr_atm_rel_store(&num_threads_, cur_thread_count + 1); |
|
|
|
|
|
|
|
|
|
thd_state_[cur_thread_count].thd = grpc_core::Thread( |
|
|
|
|
name_, &GrpcExecutor::ThreadMain, &thd_state_[cur_thread_count]); |
|
|
|
|
name_, &Executor::ThreadMain, &thd_state_[cur_thread_count]); |
|
|
|
|
thd_state_[cur_thread_count].thd.Start(); |
|
|
|
|
} |
|
|
|
|
gpr_spinlock_unlock(&adding_thread_lock_); |
|
|
|
@ -316,85 +366,52 @@ void GrpcExecutor::Enqueue(grpc_closure* closure, grpc_error* error, |
|
|
|
|
} while (retry_push); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static GrpcExecutor* executors[GRPC_NUM_EXECUTORS]; |
|
|
|
|
|
|
|
|
|
void default_enqueue_short(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[GRPC_DEFAULT_EXECUTOR]->Enqueue(closure, error, |
|
|
|
|
true /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void default_enqueue_long(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[GRPC_DEFAULT_EXECUTOR]->Enqueue(closure, error, |
|
|
|
|
false /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void resolver_enqueue_short(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[GRPC_RESOLVER_EXECUTOR]->Enqueue(closure, error, |
|
|
|
|
true /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void resolver_enqueue_long(grpc_closure* closure, grpc_error* error) { |
|
|
|
|
executors[GRPC_RESOLVER_EXECUTOR]->Enqueue(closure, error, |
|
|
|
|
false /* is_short */); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const grpc_closure_scheduler_vtable |
|
|
|
|
vtables_[GRPC_NUM_EXECUTORS][GRPC_NUM_EXECUTOR_JOB_TYPES] = { |
|
|
|
|
{{&default_enqueue_short, &default_enqueue_short, "def-ex-short"}, |
|
|
|
|
{&default_enqueue_long, &default_enqueue_long, "def-ex-long"}}, |
|
|
|
|
{{&resolver_enqueue_short, &resolver_enqueue_short, "res-ex-short"}, |
|
|
|
|
{&resolver_enqueue_long, &resolver_enqueue_long, "res-ex-long"}}}; |
|
|
|
|
|
|
|
|
|
static grpc_closure_scheduler |
|
|
|
|
schedulers_[GRPC_NUM_EXECUTORS][GRPC_NUM_EXECUTOR_JOB_TYPES] = { |
|
|
|
|
{{&vtables_[GRPC_DEFAULT_EXECUTOR][GRPC_EXECUTOR_SHORT]}, |
|
|
|
|
{&vtables_[GRPC_DEFAULT_EXECUTOR][GRPC_EXECUTOR_LONG]}}, |
|
|
|
|
{{&vtables_[GRPC_RESOLVER_EXECUTOR][GRPC_EXECUTOR_SHORT]}, |
|
|
|
|
{&vtables_[GRPC_RESOLVER_EXECUTOR][GRPC_EXECUTOR_LONG]}}}; |
|
|
|
|
|
|
|
|
|
// grpc_executor_init() and grpc_executor_shutdown() functions are called in the
|
|
|
|
|
// Executor::InitAll() and Executor::ShutdownAll() functions are called in the
|
|
|
|
|
// the grpc_init() and grpc_shutdown() code paths which are protected by a
|
|
|
|
|
// global mutex. So it is okay to assume that these functions are thread-safe
|
|
|
|
|
void grpc_executor_init() { |
|
|
|
|
EXECUTOR_TRACE0("grpc_executor_init() enter"); |
|
|
|
|
void Executor::InitAll() { |
|
|
|
|
EXECUTOR_TRACE0("Executor::InitAll() enter"); |
|
|
|
|
|
|
|
|
|
// Return if grpc_executor_init() is already called earlier
|
|
|
|
|
if (executors[GRPC_DEFAULT_EXECUTOR] != nullptr) { |
|
|
|
|
GPR_ASSERT(executors[GRPC_RESOLVER_EXECUTOR] != nullptr); |
|
|
|
|
// Return if Executor::InitAll() is already called earlier
|
|
|
|
|
if (executors[static_cast<size_t>(ExecutorType::DEFAULT)] != nullptr) { |
|
|
|
|
GPR_ASSERT(executors[static_cast<size_t>(ExecutorType::RESOLVER)] != |
|
|
|
|
nullptr); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
executors[GRPC_DEFAULT_EXECUTOR] = |
|
|
|
|
grpc_core::New<GrpcExecutor>("default-executor"); |
|
|
|
|
executors[GRPC_RESOLVER_EXECUTOR] = |
|
|
|
|
grpc_core::New<GrpcExecutor>("resolver-executor"); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)] = |
|
|
|
|
grpc_core::New<Executor>("default-executor"); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)] = |
|
|
|
|
grpc_core::New<Executor>("resolver-executor"); |
|
|
|
|
|
|
|
|
|
executors[GRPC_DEFAULT_EXECUTOR]->Init(); |
|
|
|
|
executors[GRPC_RESOLVER_EXECUTOR]->Init(); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)]->Init(); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)]->Init(); |
|
|
|
|
|
|
|
|
|
EXECUTOR_TRACE0("grpc_executor_init() done"); |
|
|
|
|
EXECUTOR_TRACE0("Executor::InitAll() done"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorType executor_type, |
|
|
|
|
GrpcExecutorJobType job_type) { |
|
|
|
|
return &schedulers_[executor_type][job_type]; |
|
|
|
|
grpc_closure_scheduler* Executor::Scheduler(ExecutorType executor_type, |
|
|
|
|
ExecutorJobType job_type) { |
|
|
|
|
return &schedulers_[static_cast<size_t>(executor_type)] |
|
|
|
|
[static_cast<size_t>(job_type)]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorJobType job_type) { |
|
|
|
|
return grpc_executor_scheduler(GRPC_DEFAULT_EXECUTOR, job_type); |
|
|
|
|
grpc_closure_scheduler* Executor::Scheduler(ExecutorJobType job_type) { |
|
|
|
|
return Executor::Scheduler(ExecutorType::DEFAULT, job_type); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_executor_shutdown() { |
|
|
|
|
EXECUTOR_TRACE0("grpc_executor_shutdown() enter"); |
|
|
|
|
void Executor::ShutdownAll() { |
|
|
|
|
EXECUTOR_TRACE0("Executor::ShutdownAll() enter"); |
|
|
|
|
|
|
|
|
|
// Return if grpc_executor_shutdown() is already called earlier
|
|
|
|
|
if (executors[GRPC_DEFAULT_EXECUTOR] == nullptr) { |
|
|
|
|
GPR_ASSERT(executors[GRPC_RESOLVER_EXECUTOR] == nullptr); |
|
|
|
|
// Return if Executor:SshutdownAll() is already called earlier
|
|
|
|
|
if (executors[static_cast<size_t>(ExecutorType::DEFAULT)] == nullptr) { |
|
|
|
|
GPR_ASSERT(executors[static_cast<size_t>(ExecutorType::RESOLVER)] == |
|
|
|
|
nullptr); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
executors[GRPC_DEFAULT_EXECUTOR]->Shutdown(); |
|
|
|
|
executors[GRPC_RESOLVER_EXECUTOR]->Shutdown(); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)]->Shutdown(); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)]->Shutdown(); |
|
|
|
|
|
|
|
|
|
// Delete the executor objects.
|
|
|
|
|
//
|
|
|
|
@ -408,26 +425,36 @@ void grpc_executor_shutdown() { |
|
|
|
|
// By ensuring that all executors are shutdown first, we are also ensuring
|
|
|
|
|
// that no thread is active across all executors.
|
|
|
|
|
|
|
|
|
|
grpc_core::Delete<GrpcExecutor>(executors[GRPC_DEFAULT_EXECUTOR]); |
|
|
|
|
grpc_core::Delete<GrpcExecutor>(executors[GRPC_RESOLVER_EXECUTOR]); |
|
|
|
|
executors[GRPC_DEFAULT_EXECUTOR] = nullptr; |
|
|
|
|
executors[GRPC_RESOLVER_EXECUTOR] = nullptr; |
|
|
|
|
grpc_core::Delete<Executor>( |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)]); |
|
|
|
|
grpc_core::Delete<Executor>( |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)]); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)] = nullptr; |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::RESOLVER)] = nullptr; |
|
|
|
|
|
|
|
|
|
EXECUTOR_TRACE0("grpc_executor_shutdown() done"); |
|
|
|
|
EXECUTOR_TRACE0("Executor::ShutdownAll() done"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool grpc_executor_is_threaded(GrpcExecutorType executor_type) { |
|
|
|
|
GPR_ASSERT(executor_type < GRPC_NUM_EXECUTORS); |
|
|
|
|
return executors[executor_type]->IsThreaded(); |
|
|
|
|
bool Executor::IsThreaded(ExecutorType executor_type) { |
|
|
|
|
GPR_ASSERT(executor_type < ExecutorType::NUM_EXECUTORS); |
|
|
|
|
return executors[static_cast<size_t>(executor_type)]->IsThreaded(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool grpc_executor_is_threaded() { |
|
|
|
|
return grpc_executor_is_threaded(GRPC_DEFAULT_EXECUTOR); |
|
|
|
|
bool Executor::IsThreadedDefault() { |
|
|
|
|
return Executor::IsThreaded(ExecutorType::DEFAULT); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_executor_set_threading(bool enable) { |
|
|
|
|
EXECUTOR_TRACE("grpc_executor_set_threading(%d) called", enable); |
|
|
|
|
for (int i = 0; i < GRPC_NUM_EXECUTORS; i++) { |
|
|
|
|
void Executor::SetThreadingAll(bool enable) { |
|
|
|
|
EXECUTOR_TRACE("Executor::SetThreadingAll(%d) called", enable); |
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(ExecutorType::NUM_EXECUTORS); |
|
|
|
|
i++) { |
|
|
|
|
executors[i]->SetThreading(enable); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Executor::SetThreadingDefault(bool enable) { |
|
|
|
|
EXECUTOR_TRACE("Executor::SetThreadingDefault(%d) called", enable); |
|
|
|
|
executors[static_cast<size_t>(ExecutorType::DEFAULT)]->SetThreading(enable); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace grpc_core
|
|
|
|
|