/* * * 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 #ifdef GPR_POSIX_SYNC #include "src/core/lib/gprpp/thd.h" #include #include #include #include #include #include #include #include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/fork.h" #include "src/core/lib/gprpp/memory.h" namespace grpc_core { namespace { 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(malloc(sizeof(*info))); GPR_ASSERT(info != nullptr); info->thread = this; info->body = thd_body; info->arg = arg; info->name = thd_name; grpc_core::Fork::IncThreadCount(); 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(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); grpc_core::Fork::DecThreadCount(); 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); grpc_core::Fork::DecThreadCount(); } }; ~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: 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(thd_name, 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 // 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 */