/*
*
* 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:
/// 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.
Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
bool* success = nullptr);
/// 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_) {
other.state_ = MOVED;
other.impl_ = nullptr;
}
/// 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_;
other.state_ = MOVED;
other.impl_ = nullptr;
}
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. The destructor shouldn't have to do anything.
~Thread() { GPR_ASSERT(impl_ == nullptr); }
void Start() {
if (impl_ != nullptr) {
GPR_ASSERT(state_ == ALIVE);
state_ = STARTED;
impl_->Start();
} else {
GPR_ASSERT(state_ == FAILED);
}
};
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_;
};
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_GPRPP_THD_H */