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)
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) {
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