1 /* 2 * 3 * Copyright 2015 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 #ifndef GRPC_CORE_LIB_GPRPP_THD_H 20 #define GRPC_CORE_LIB_GPRPP_THD_H 21 22 /** Internal thread interface. */ 23 24 #include <grpc/support/port_platform.h> 25 26 #include <grpc/support/log.h> 27 #include <grpc/support/sync.h> 28 #include <grpc/support/thd_id.h> 29 #include <grpc/support/time.h> 30 31 #include "src/core/lib/gprpp/memory.h" 32 33 namespace grpc_core { 34 namespace internal { 35 36 /// Base class for platform-specific thread-state 37 class ThreadInternalsInterface { 38 public: ~ThreadInternalsInterface()39 virtual ~ThreadInternalsInterface() {} 40 virtual void Start() = 0; 41 virtual void Join() = 0; 42 }; 43 44 } // namespace internal 45 46 class Thread { 47 public: 48 class Options { 49 public: Options()50 Options() : joinable_(true), tracked_(true), stack_size_(0) {} 51 /// Set whether the thread is joinable or detached. set_joinable(bool joinable)52 Options& set_joinable(bool joinable) { 53 joinable_ = joinable; 54 return *this; 55 } joinable()56 bool joinable() const { return joinable_; } 57 58 /// Set whether the thread is tracked for fork support. set_tracked(bool tracked)59 Options& set_tracked(bool tracked) { 60 tracked_ = tracked; 61 return *this; 62 } tracked()63 bool tracked() const { return tracked_; } 64 65 /// Sets thread stack size (in bytes). Sets to 0 will use the default stack 66 /// size which is 64KB for Windows threads and 2MB for Posix(x86) threads. set_stack_size(size_t bytes)67 Options& set_stack_size(size_t bytes) { 68 stack_size_ = bytes; 69 return *this; 70 } stack_size()71 size_t stack_size() const { return stack_size_; } 72 73 private: 74 bool joinable_; 75 bool tracked_; 76 size_t stack_size_; 77 }; 78 /// Default constructor only to allow use in structs that lack constructors 79 /// Does not produce a validly-constructed thread; must later 80 /// use placement new to construct a real thread. Does not init mu_ and cv_ Thread()81 Thread() : state_(FAKE), impl_(nullptr) {} 82 83 /// Normal constructor to create a thread with name \a thd_name, 84 /// which will execute a thread based on function \a thd_body 85 /// with argument \a arg once it is started. 86 /// The optional \a success argument indicates whether the thread 87 /// is successfully created. 88 /// The optional \a options can be used to set the thread detachable. 89 Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, 90 bool* success = nullptr, const Options& options = Options()); 91 92 /// Move constructor for thread. After this is called, the other thread 93 /// no longer represents a living thread object Thread(Thread && other)94 Thread(Thread&& other) noexcept 95 : state_(other.state_), impl_(other.impl_), options_(other.options_) { 96 other.state_ = MOVED; 97 other.impl_ = nullptr; 98 other.options_ = Options(); 99 } 100 101 /// Move assignment operator for thread. After this is called, the other 102 /// thread no longer represents a living thread object. Not allowed if this 103 /// thread actually exists 104 Thread& operator=(Thread&& other) noexcept { 105 if (this != &other) { 106 // TODO(vjpai): if we can be sure that all Thread's are actually 107 // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here. 108 // However, as long as threads come in structures that are 109 // allocated via gpr_malloc, this will not be the case, so we cannot 110 // assert it for the time being. 111 state_ = other.state_; 112 impl_ = other.impl_; 113 options_ = other.options_; 114 other.state_ = MOVED; 115 other.impl_ = nullptr; 116 other.options_ = Options(); 117 } 118 return *this; 119 } 120 121 /// The destructor is strictly optional; either the thread never came to life 122 /// and the constructor itself killed it, or it has already been joined and 123 /// the Join function kills it, or it was detached (non-joinable) and it has 124 /// run to completion and is now killing itself. The destructor shouldn't have 125 /// to do anything. ~Thread()126 ~Thread() { GPR_ASSERT(!options_.joinable() || impl_ == nullptr); } 127 Start()128 void Start() { 129 if (impl_ != nullptr) { 130 GPR_ASSERT(state_ == ALIVE); 131 state_ = STARTED; 132 impl_->Start(); 133 // If the Thread is not joinable, then the impl_ will cause the deletion 134 // of this Thread object when the thread function completes. Since no 135 // other operation is allowed to a detached thread after Start, there is 136 // no need to change the value of the impl_ or state_ . The next operation 137 // on this object will be the deletion, which will trigger the destructor. 138 } else { 139 GPR_ASSERT(state_ == FAILED); 140 } 141 } 142 143 // It is only legal to call Join if the Thread is created as joinable. Join()144 void Join() { 145 if (impl_ != nullptr) { 146 impl_->Join(); 147 delete impl_; 148 state_ = DONE; 149 impl_ = nullptr; 150 } else { 151 GPR_ASSERT(state_ == FAILED); 152 } 153 } 154 155 private: 156 Thread(const Thread&) = delete; 157 Thread& operator=(const Thread&) = delete; 158 159 /// The thread states are as follows: 160 /// FAKE -- just a dummy placeholder Thread created by the default constructor 161 /// ALIVE -- an actual thread of control exists associated with this thread 162 /// STARTED -- the thread of control has been started 163 /// DONE -- the thread of control has completed and been joined 164 /// FAILED -- the thread of control never came alive 165 /// MOVED -- contents were moved out and we're no longer tracking them 166 enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED }; 167 ThreadState state_; 168 internal::ThreadInternalsInterface* impl_; 169 Options options_; 170 }; 171 172 } // namespace grpc_core 173 174 #endif /* GRPC_CORE_LIB_GPRPP_THD_H */ 175