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_SRC_CORE_LIB_GPRPP_THD_H 20 #define GRPC_SRC_CORE_LIB_GPRPP_THD_H 21 22 /// Internal thread interface. 23 24 #include <grpc/support/port_platform.h> 25 26 #include <stddef.h> 27 28 #include <memory> 29 #include <utility> 30 31 #include "absl/functional/any_invocable.h" 32 33 #include <grpc/support/log.h> 34 #include <grpc/support/thd_id.h> 35 36 namespace grpc_core { 37 namespace internal { 38 39 /// Base class for platform-specific thread-state 40 class ThreadInternalsInterface { 41 public: ~ThreadInternalsInterface()42 virtual ~ThreadInternalsInterface() {} 43 virtual void Start() = 0; 44 virtual void Join() = 0; 45 }; 46 47 } // namespace internal 48 49 class Thread { 50 public: 51 // Send a signal to the thread. 52 // This is not supported on all platforms 53 static void Signal(gpr_thd_id tid, int sig); 54 // Kill the running thread. Likely not a clean operation. 55 // This is not supported on all platforms. 56 static void Kill(gpr_thd_id tid); 57 58 class Options { 59 public: Options()60 Options() : joinable_(true), tracked_(true), stack_size_(0) {} 61 /// Set whether the thread is joinable or detached. set_joinable(bool joinable)62 Options& set_joinable(bool joinable) { 63 joinable_ = joinable; 64 return *this; 65 } joinable()66 bool joinable() const { return joinable_; } 67 68 /// Set whether the thread is tracked for fork support. set_tracked(bool tracked)69 Options& set_tracked(bool tracked) { 70 tracked_ = tracked; 71 return *this; 72 } tracked()73 bool tracked() const { return tracked_; } 74 75 /// Sets thread stack size (in bytes). Sets to 0 will use the default stack 76 /// size which is 64KB for Windows threads and 2MB for Posix(x86) threads. set_stack_size(size_t bytes)77 Options& set_stack_size(size_t bytes) { 78 stack_size_ = bytes; 79 return *this; 80 } stack_size()81 size_t stack_size() const { return stack_size_; } 82 83 private: 84 bool joinable_; 85 bool tracked_; 86 size_t stack_size_; 87 }; 88 /// Default constructor only to allow use in structs that lack constructors 89 /// Does not produce a validly-constructed thread; must later 90 /// use placement new to construct a real thread. Does not init mu_ and cv_ Thread()91 Thread() : state_(FAKE), impl_(nullptr) {} 92 93 /// Normal constructor to create a thread with name \a thd_name, 94 /// which will execute a thread based on function \a thd_body 95 /// with argument \a arg once it is started. 96 /// The optional \a success argument indicates whether the thread 97 /// is successfully created. 98 /// The optional \a options can be used to set the thread detachable. 99 Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, 100 bool* success = nullptr, const Options& options = Options()); 101 102 Thread(const char* thd_name, absl::AnyInvocable<void()> fn, 103 bool* success = nullptr, const Options& options = Options()) 104 : Thread( 105 thd_name, 106 [](void* p) { 107 std::unique_ptr<absl::AnyInvocable<void()>> fn_from_p( 108 static_cast<absl::AnyInvocable<void()>*>(p)); 109 (*fn_from_p)(); 110 }, 111 new absl::AnyInvocable<void()>(std::move(fn)), success, options) {} 112 113 /// Move constructor for thread. After this is called, the other thread 114 /// no longer represents a living thread object Thread(Thread && other)115 Thread(Thread&& other) noexcept 116 : state_(other.state_), impl_(other.impl_), options_(other.options_) { 117 other.state_ = MOVED; 118 other.impl_ = nullptr; 119 other.options_ = Options(); 120 } 121 122 /// Move assignment operator for thread. After this is called, the other 123 /// thread no longer represents a living thread object. Not allowed if this 124 /// thread actually exists 125 Thread& operator=(Thread&& other) noexcept { 126 if (this != &other) { 127 // TODO(vjpai): if we can be sure that all Thread's are actually 128 // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here. 129 // However, as long as threads come in structures that are 130 // allocated via gpr_malloc, this will not be the case, so we cannot 131 // assert it for the time being. 132 state_ = other.state_; 133 impl_ = other.impl_; 134 options_ = other.options_; 135 other.state_ = MOVED; 136 other.impl_ = nullptr; 137 other.options_ = Options(); 138 } 139 return *this; 140 } 141 142 /// The destructor is strictly optional; either the thread never came to life 143 /// and the constructor itself killed it, or it has already been joined and 144 /// the Join function kills it, or it was detached (non-joinable) and it has 145 /// run to completion and is now killing itself. The destructor shouldn't have 146 /// to do anything. ~Thread()147 ~Thread() { GPR_ASSERT(!options_.joinable() || impl_ == nullptr); } 148 Start()149 void Start() { 150 if (impl_ != nullptr) { 151 GPR_ASSERT(state_ == ALIVE); 152 state_ = STARTED; 153 impl_->Start(); 154 // If the Thread is not joinable, then the impl_ will cause the deletion 155 // of this Thread object when the thread function completes. Since no 156 // other operation is allowed to a detached thread after Start, there is 157 // no need to change the value of the impl_ or state_ . The next operation 158 // on this object will be the deletion, which will trigger the destructor. 159 } else { 160 GPR_ASSERT(state_ == FAILED); 161 } 162 } 163 164 // It is only legal to call Join if the Thread is created as joinable. Join()165 void Join() { 166 if (impl_ != nullptr) { 167 impl_->Join(); 168 delete impl_; 169 state_ = DONE; 170 impl_ = nullptr; 171 } else { 172 GPR_ASSERT(state_ == FAILED); 173 } 174 } 175 176 private: 177 Thread(const Thread&) = delete; 178 Thread& operator=(const Thread&) = delete; 179 180 /// The thread states are as follows: 181 /// FAKE -- just a phony placeholder Thread created by the default constructor 182 /// ALIVE -- an actual thread of control exists associated with this thread 183 /// STARTED -- the thread of control has been started 184 /// DONE -- the thread of control has completed and been joined 185 /// FAILED -- the thread of control never came alive 186 /// MOVED -- contents were moved out and we're no longer tracking them 187 enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED }; 188 ThreadState state_; 189 internal::ThreadInternalsInterface* impl_; 190 Options options_; 191 }; 192 193 } // namespace grpc_core 194 195 #endif // GRPC_SRC_CORE_LIB_GPRPP_THD_H 196