--- /dev/null
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_THD_H
+#define GRPC_CORE_LIB_GPRPP_THD_H
+
+/** Internal thread interface. */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+namespace internal {
+
+/// Base class for platform-specific thread-state
+class ThreadInternalsInterface {
+ public:
+ virtual ~ThreadInternalsInterface() {}
+ virtual void Start() GRPC_ABSTRACT;
+ virtual void Join() GRPC_ABSTRACT;
+ GRPC_ABSTRACT_BASE_CLASS
+};
+
+} // namespace internal
+
+class Thread {
+ public:
+ class Options {
+ public:
+ Options() : joinable_(true), tracked_(true), stack_size_(0) {}
+ /// Set whether the thread is joinable or detached.
+ Options& set_joinable(bool joinable) {
+ joinable_ = joinable;
+ return *this;
+ }
+ bool joinable() const { return joinable_; }
+
+ /// Set whether the thread is tracked for fork support.
+ Options& set_tracked(bool tracked) {
+ tracked_ = tracked;
+ return *this;
+ }
+ bool tracked() const { return tracked_; }
+
+ /// Sets thread stack size (in bytes). Sets to 0 will use the default stack
+ /// size which is 64KB for Windows threads and 2MB for Posix(x86) threads.
+ Options& set_stack_size(size_t bytes) {
+ stack_size_ = bytes;
+ return *this;
+ }
+ size_t stack_size() const { return stack_size_; }
+
+ private:
+ bool joinable_;
+ bool tracked_;
+ size_t stack_size_;
+ };
+ /// Default constructor only to allow use in structs that lack constructors
+ /// Does not produce a validly-constructed thread; must later
+ /// use placement new to construct a real thread. Does not init mu_ and cv_
+ Thread() : state_(FAKE), impl_(nullptr) {}
+
+ /// Normal constructor to create a thread with name \a thd_name,
+ /// which will execute a thread based on function \a thd_body
+ /// with argument \a arg once it is started.
+ /// The optional \a success argument indicates whether the thread
+ /// is successfully created.
+ /// The optional \a options can be used to set the thread detachable.
+ Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+ bool* success = nullptr, const Options& options = Options());
+
+ /// Move constructor for thread. After this is called, the other thread
+ /// no longer represents a living thread object
+ Thread(Thread&& other)
+ : state_(other.state_), impl_(other.impl_), options_(other.options_) {
+ other.state_ = MOVED;
+ other.impl_ = nullptr;
+ other.options_ = Options();
+ }
+
+ /// Move assignment operator for thread. After this is called, the other
+ /// thread no longer represents a living thread object. Not allowed if this
+ /// thread actually exists
+ Thread& operator=(Thread&& other) {
+ if (this != &other) {
+ // TODO(vjpai): if we can be sure that all Thread's are actually
+ // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here.
+ // However, as long as threads come in structures that are
+ // allocated via gpr_malloc, this will not be the case, so we cannot
+ // assert it for the time being.
+ state_ = other.state_;
+ impl_ = other.impl_;
+ options_ = other.options_;
+ other.state_ = MOVED;
+ other.impl_ = nullptr;
+ other.options_ = Options();
+ }
+ return *this;
+ }
+
+ /// The destructor is strictly optional; either the thread never came to life
+ /// and the constructor itself killed it, or it has already been joined and
+ /// the Join function kills it, or it was detached (non-joinable) and it has
+ /// run to completion and is now killing itself. The destructor shouldn't have
+ /// to do anything.
+ ~Thread() { GPR_ASSERT(!options_.joinable() || impl_ == nullptr); }
+
+ void Start() {
+ if (impl_ != nullptr) {
+ GPR_ASSERT(state_ == ALIVE);
+ state_ = STARTED;
+ impl_->Start();
+ // If the Thread is not joinable, then the impl_ will cause the deletion
+ // of this Thread object when the thread function completes. Since no
+ // other operation is allowed to a detached thread after Start, there is
+ // no need to change the value of the impl_ or state_ . The next operation
+ // on this object will be the deletion, which will trigger the destructor.
+ } else {
+ GPR_ASSERT(state_ == FAILED);
+ }
+ }
+
+ // It is only legal to call Join if the Thread is created as joinable.
+ void Join() {
+ if (impl_ != nullptr) {
+ impl_->Join();
+ grpc_core::Delete(impl_);
+ state_ = DONE;
+ impl_ = nullptr;
+ } else {
+ GPR_ASSERT(state_ == FAILED);
+ }
+ }
+
+ private:
+ Thread(const Thread&) = delete;
+ Thread& operator=(const Thread&) = delete;
+
+ /// The thread states are as follows:
+ /// FAKE -- just a dummy placeholder Thread created by the default constructor
+ /// ALIVE -- an actual thread of control exists associated with this thread
+ /// STARTED -- the thread of control has been started
+ /// DONE -- the thread of control has completed and been joined
+ /// FAILED -- the thread of control never came alive
+ /// MOVED -- contents were moved out and we're no longer tracking them
+ enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED };
+ ThreadState state_;
+ internal::ThreadInternalsInterface* impl_;
+ Options options_;
+};
+
+} // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_THD_H */