| /* |
| * |
| * Copyright 2015 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #ifndef GRPC_CORE_LIB_GPRPP_THD_H |
| #define GRPC_CORE_LIB_GPRPP_THD_H |
| |
| /** Internal thread interface. */ |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include <grpc/support/log.h> |
| #include <grpc/support/sync.h> |
| #include <grpc/support/thd_id.h> |
| #include <grpc/support/time.h> |
| |
| #include "src/core/lib/gprpp/memory.h" |
| |
| namespace grpc_core { |
| namespace internal { |
| |
| /// Base class for platform-specific thread-state |
| class ThreadInternalsInterface { |
| public: |
| virtual ~ThreadInternalsInterface() {} |
| virtual void Start() = 0; |
| virtual void Join() = 0; |
| }; |
| |
| } // namespace internal |
| |
| class Thread { |
| public: |
| class Options { |
| public: |
| Options() : joinable_(true), tracked_(true), stack_size_(0) {} |
| /// Set whether the thread is joinable or detached. |
| Options& set_joinable(bool joinable) { |
| joinable_ = joinable; |
| return *this; |
| } |
| bool joinable() const { return joinable_; } |
| |
| /// Set whether the thread is tracked for fork support. |
| Options& set_tracked(bool tracked) { |
| tracked_ = tracked; |
| return *this; |
| } |
| bool tracked() const { return tracked_; } |
| |
| /// Sets thread stack size (in bytes). Sets to 0 will use the default stack |
| /// size which is 64KB for Windows threads and 2MB for Posix(x86) threads. |
| Options& set_stack_size(size_t bytes) { |
| stack_size_ = bytes; |
| return *this; |
| } |
| size_t stack_size() const { return stack_size_; } |
| |
| private: |
| bool joinable_; |
| bool tracked_; |
| size_t stack_size_; |
| }; |
| /// Default constructor only to allow use in structs that lack constructors |
| /// Does not produce a validly-constructed thread; must later |
| /// use placement new to construct a real thread. Does not init mu_ and cv_ |
| Thread() : state_(FAKE), impl_(nullptr) {} |
| |
| /// Normal constructor to create a thread with name \a thd_name, |
| /// which will execute a thread based on function \a thd_body |
| /// with argument \a arg once it is started. |
| /// The optional \a success argument indicates whether the thread |
| /// is successfully created. |
| /// The optional \a options can be used to set the thread detachable. |
| Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, |
| bool* success = nullptr, const Options& options = Options()); |
| |
| /// Move constructor for thread. After this is called, the other thread |
| /// no longer represents a living thread object |
| Thread(Thread&& other) noexcept |
| : state_(other.state_), impl_(other.impl_), options_(other.options_) { |
| other.state_ = MOVED; |
| other.impl_ = nullptr; |
| other.options_ = Options(); |
| } |
| |
| /// Move assignment operator for thread. After this is called, the other |
| /// thread no longer represents a living thread object. Not allowed if this |
| /// thread actually exists |
| Thread& operator=(Thread&& other) noexcept { |
| if (this != &other) { |
| // TODO(vjpai): if we can be sure that all Thread's are actually |
| // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here. |
| // However, as long as threads come in structures that are |
| // allocated via gpr_malloc, this will not be the case, so we cannot |
| // assert it for the time being. |
| state_ = other.state_; |
| impl_ = other.impl_; |
| options_ = other.options_; |
| other.state_ = MOVED; |
| other.impl_ = nullptr; |
| other.options_ = Options(); |
| } |
| return *this; |
| } |
| |
| /// The destructor is strictly optional; either the thread never came to life |
| /// and the constructor itself killed it, or it has already been joined and |
| /// the Join function kills it, or it was detached (non-joinable) and it has |
| /// run to completion and is now killing itself. The destructor shouldn't have |
| /// to do anything. |
| ~Thread() { GPR_ASSERT(!options_.joinable() || impl_ == nullptr); } |
| |
| void Start() { |
| if (impl_ != nullptr) { |
| GPR_ASSERT(state_ == ALIVE); |
| state_ = STARTED; |
| impl_->Start(); |
| // If the Thread is not joinable, then the impl_ will cause the deletion |
| // of this Thread object when the thread function completes. Since no |
| // other operation is allowed to a detached thread after Start, there is |
| // no need to change the value of the impl_ or state_ . The next operation |
| // on this object will be the deletion, which will trigger the destructor. |
| } else { |
| GPR_ASSERT(state_ == FAILED); |
| } |
| } |
| |
| // It is only legal to call Join if the Thread is created as joinable. |
| void Join() { |
| if (impl_ != nullptr) { |
| impl_->Join(); |
| delete impl_; |
| state_ = DONE; |
| impl_ = nullptr; |
| } else { |
| GPR_ASSERT(state_ == FAILED); |
| } |
| } |
| |
| private: |
| Thread(const Thread&) = delete; |
| Thread& operator=(const Thread&) = delete; |
| |
| /// The thread states are as follows: |
| /// FAKE -- just a phony placeholder Thread created by the default constructor |
| /// ALIVE -- an actual thread of control exists associated with this thread |
| /// STARTED -- the thread of control has been started |
| /// DONE -- the thread of control has completed and been joined |
| /// FAILED -- the thread of control never came alive |
| /// MOVED -- contents were moved out and we're no longer tracking them |
| enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED }; |
| ThreadState state_; |
| internal::ThreadInternalsInterface* impl_; |
| Options options_; |
| }; |
| |
| } // namespace grpc_core |
| |
| #endif /* GRPC_CORE_LIB_GPRPP_THD_H */ |