• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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