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