diff options
Diffstat (limited to 'runtime/thread.h')
-rw-r--r-- | runtime/thread.h | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/runtime/thread.h b/runtime/thread.h new file mode 100644 index 0000000..0daf763 --- /dev/null +++ b/runtime/thread.h @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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 ART_SRC_THREAD_H_ +#define ART_SRC_THREAD_H_ + +#include <pthread.h> + +#include <bitset> +#include <deque> +#include <iosfwd> +#include <list> +#include <string> + +#include "base/macros.h" +#include "globals.h" +#include "jvalue.h" +#include "oat/runtime/oat_support_entrypoints.h" +#include "locks.h" +#include "offsets.h" +#include "root_visitor.h" +#include "runtime_stats.h" +#include "stack.h" +#include "stack_indirect_reference_table.h" +#include "thread_state.h" +#include "throw_location.h" +#include "UniquePtr.h" + +namespace art { + +namespace mirror { +class AbstractMethod; +class Array; +class Class; +class ClassLoader; +class Object; +template<class T> class ObjectArray; +template<class T> class PrimitiveArray; +typedef PrimitiveArray<int32_t> IntArray; +class StackTraceElement; +class StaticStorageBase; +class Throwable; +} // namespace mirror +class BaseMutex; +class ClassLinker; +class Closure; +class Context; +struct DebugInvokeReq; +class DexFile; +struct JavaVMExt; +struct JNIEnvExt; +class Monitor; +class Runtime; +class ScopedObjectAccess; +class ScopedObjectAccessUnchecked; +class ShadowFrame; +class Thread; +class ThreadList; + +// Thread priorities. These must match the Thread.MIN_PRIORITY, +// Thread.NORM_PRIORITY, and Thread.MAX_PRIORITY constants. +enum ThreadPriority { + kMinThreadPriority = 1, + kNormThreadPriority = 5, + kMaxThreadPriority = 10, +}; + +enum ThreadFlag { + kSuspendRequest = 1, // If set implies that suspend_count_ > 0 and the Thread should enter the + // safepoint handler. + kCheckpointRequest = 2 // Request that the thread do some checkpoint work and then continue. +}; + +class PACKED(4) Thread { + public: + // Space to throw a StackOverflowError in. + static const size_t kStackOverflowReservedBytes = 16 * KB; + + // Creates a new native thread corresponding to the given managed peer. + // Used to implement Thread.start. + static void CreateNativeThread(JNIEnv* env, jobject peer, size_t stack_size, bool daemon); + + // Attaches the calling native thread to the runtime, returning the new native peer. + // Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls. + static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_group, + bool create_peer); + + // Reset internal state of child thread after fork. + void InitAfterFork(); + + static Thread* Current() { + // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious + // that we can replace this with a direct %fs access on x86. + if(!is_started_) { + return NULL; + } else { + void* thread = pthread_getspecific(Thread::pthread_key_self_); + return reinterpret_cast<Thread*>(thread); + } + } + + static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts, + mirror::Object* thread_peer) + EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts, jobject thread) + EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Translates 172 to pAllocArrayFromCode and so on. + static void DumpThreadOffset(std::ostream& os, uint32_t offset, size_t size_of_pointers); + + // Dumps a one-line summary of thread state (used for operator<<). + void ShortDump(std::ostream& os) const; + + // Dumps the detailed thread state and the thread stack (used for SIGQUIT). + void Dump(std::ostream& os) const + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Dumps the SIGQUIT per-thread header. 'thread' can be NULL for a non-attached thread, in which + // case we use 'tid' to identify the thread, and we'll include as much information as we can. + static void DumpState(std::ostream& os, const Thread* thread, pid_t tid) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ThreadState GetState() const { + return static_cast<ThreadState>(state_and_flags_.as_struct.state); + } + + ThreadState SetState(ThreadState new_state); + + int GetSuspendCount() const EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { + return suspend_count_; + } + + int GetDebugSuspendCount() const EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { + return debug_suspend_count_; + } + + bool IsSuspended() const { + union StateAndFlags state_and_flags = state_and_flags_; + return state_and_flags.as_struct.state != kRunnable && + (state_and_flags.as_struct.flags & kSuspendRequest) != 0; + } + + void ModifySuspendCount(Thread* self, int delta, bool for_debugger) + EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_); + + bool RequestCheckpoint(Closure* function); + + // Called when thread detected that the thread_suspend_count_ was non-zero. Gives up share of + // mutator_lock_ and waits until it is resumed and thread_suspend_count_ is zero. + void FullSuspendCheck() + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Transition from non-runnable to runnable state acquiring share on mutator_lock_. + ThreadState TransitionFromSuspendedToRunnable() + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCK_FUNCTION(Locks::mutator_lock_) + ALWAYS_INLINE; + + // Transition from runnable into a state where mutator privileges are denied. Releases share of + // mutator lock. + void TransitionFromRunnableToSuspended(ThreadState new_state) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + UNLOCK_FUNCTION(Locks::mutator_lock_) + ALWAYS_INLINE; + + // Wait for a debugger suspension on the thread associated with the given peer. Returns the + // thread on success, else NULL. If the thread should be suspended then request_suspension should + // be true on entry. If the suspension times out then *timeout is set to true. + static Thread* SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out) + LOCKS_EXCLUDED(Locks::mutator_lock_, + Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_); + + // Once called thread suspension will cause an assertion failure. +#ifndef NDEBUG + const char* StartAssertNoThreadSuspension(const char* cause) { + CHECK(cause != NULL); + const char* previous_cause = last_no_thread_suspension_cause_; + no_thread_suspension_++; + last_no_thread_suspension_cause_ = cause; + return previous_cause; + } +#else + const char* StartAssertNoThreadSuspension(const char* cause) { + CHECK(cause != NULL); + return NULL; + } +#endif + + // End region where no thread suspension is expected. +#ifndef NDEBUG + void EndAssertNoThreadSuspension(const char* old_cause) { + CHECK(old_cause != NULL || no_thread_suspension_ == 1); + CHECK_GT(no_thread_suspension_, 0U); + no_thread_suspension_--; + last_no_thread_suspension_cause_ = old_cause; + } +#else + void EndAssertNoThreadSuspension(const char*) { + } +#endif + + + void AssertThreadSuspensionIsAllowable(bool check_locks = true) const; + + bool IsDaemon() const { + return daemon_; + } + + bool HoldsLock(mirror::Object*); + + /* + * Changes the priority of this thread to match that of the java.lang.Thread object. + * + * We map a priority value from 1-10 to Linux "nice" values, where lower + * numbers indicate higher priority. + */ + void SetNativePriority(int newPriority); + + /* + * Returns the thread priority for the current thread by querying the system. + * This is useful when attaching a thread through JNI. + * + * Returns a value from 1 to 10 (compatible with java.lang.Thread values). + */ + static int GetNativePriority(); + + uint32_t GetThinLockId() const { + return thin_lock_id_; + } + + pid_t GetTid() const { + return tid_; + } + + // Returns the java.lang.Thread's name, or NULL if this Thread* doesn't have a peer. + mirror::String* GetThreadName(const ScopedObjectAccessUnchecked& ts) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Sets 'name' to the java.lang.Thread's name. This requires no transition to managed code, + // allocation, or locking. + void GetThreadName(std::string& name) const; + + // Sets the thread's name. + void SetThreadName(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::Object* GetPeer() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(jpeer_ == NULL); + return opeer_; + } + + bool HasPeer() const { + return jpeer_ != NULL || opeer_ != NULL; + } + + RuntimeStats* GetStats() { + return &stats_; + } + + bool IsStillStarting() const; + + bool IsExceptionPending() const { + return exception_ != NULL; + } + + mirror::Throwable* GetException(ThrowLocation* throw_location) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (throw_location != NULL) { + *throw_location = throw_location_; + } + return exception_; + } + + void AssertNoPendingException() const; + + void SetException(const ThrowLocation& throw_location, mirror::Throwable* new_exception) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(new_exception != NULL); + // TODO: DCHECK(!IsExceptionPending()); + exception_ = new_exception; + throw_location_ = throw_location; + } + + void ClearException() { + exception_ = NULL; + throw_location_.Clear(); + } + + // Find catch block and perform long jump to appropriate exception handle + void QuickDeliverException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + Context* GetLongJumpContext(); + void ReleaseLongJumpContext(Context* context) { + DCHECK(long_jump_context_ == NULL); + long_jump_context_ = context; + } + + mirror::AbstractMethod* GetCurrentMethod(uint32_t* dex_pc) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ThrowLocation GetCurrentLocationForThrow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void SetTopOfStack(void* stack, uintptr_t pc) { + mirror::AbstractMethod** top_method = reinterpret_cast<mirror::AbstractMethod**>(stack); + managed_stack_.SetTopQuickFrame(top_method); + managed_stack_.SetTopQuickFramePc(pc); + } + + void SetTopOfShadowStack(ShadowFrame* top) { + managed_stack_.SetTopShadowFrame(top); + } + + bool HasManagedStack() const { + return managed_stack_.GetTopQuickFrame() != NULL || managed_stack_.GetTopShadowFrame() != NULL; + } + + // If 'msg' is NULL, no detail message is set. + void ThrowNewException(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* msg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // If 'msg' is NULL, no detail message is set. An exception must be pending, and will be + // used as the new exception's cause. + void ThrowNewWrappedException(const ThrowLocation& throw_location, + const char* exception_class_descriptor, + const char* msg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void ThrowNewExceptionF(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* fmt, ...) + __attribute__((format(printf, 4, 5))) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void ThrowNewExceptionV(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* fmt, va_list ap) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // OutOfMemoryError is special, because we need to pre-allocate an instance. + // Only the GC should call this. + void ThrowOutOfMemoryError(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void Startup(); + static void FinishStartup(); + static void Shutdown(); + + // JNI methods + JNIEnvExt* GetJniEnv() const { + return jni_env_; + } + + // Convert a jobject into a Object* + mirror::Object* DecodeJObject(jobject obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Implements java.lang.Thread.interrupted. + bool Interrupted(); + // Implements java.lang.Thread.isInterrupted. + bool IsInterrupted(); + void Interrupt(); + void Notify(); + + mirror::ClassLoader* GetClassLoaderOverride() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return class_loader_override_; + } + + void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) { + class_loader_override_ = class_loader_override; + } + + // Create the internal representation of a stack trace, that is more time + // and space efficient to compute than the StackTraceElement[] + jobject CreateInternalStackTrace(const ScopedObjectAccessUnchecked& soa) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Convert an internal stack trace representation (returned by CreateInternalStackTrace) to a + // StackTraceElement[]. If output_array is NULL, a new array is created, otherwise as many + // frames as will fit are written into the given array. If stack_depth is non-NULL, it's updated + // with the number of valid frames in the returned array. + static jobjectArray InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal, + jobjectArray output_array = NULL, int* stack_depth = NULL); + + void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void VerifyRoots(VerifyRootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // + // Offsets of various members of native Thread class, used by compiled code. + // + + static ThreadOffset SelfOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, self_)); + } + + static ThreadOffset ExceptionOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_)); + } + + static ThreadOffset PeerOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, opeer_)); + } + + static ThreadOffset ThinLockIdOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_)); + } + + static ThreadOffset CardTableOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, card_table_)); + } + + static ThreadOffset ThreadFlagsOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, state_and_flags_)); + } + + // Size of stack less any space reserved for stack overflow + size_t GetStackSize() const { + return stack_size_ - (stack_end_ - stack_begin_); + } + + byte* GetStackEnd() const { + return stack_end_; + } + + // Set the stack end to that to be used during a stack overflow + void SetStackEndForStackOverflow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Set the stack end to that to be used during regular execution + void ResetDefaultStackEnd() { + // Our stacks grow down, so we want stack_end_ to be near there, but reserving enough room + // to throw a StackOverflowError. + stack_end_ = stack_begin_ + kStackOverflowReservedBytes; + } + + bool IsHandlingStackOverflow() const { + return stack_end_ == stack_begin_; + } + + static ThreadOffset StackEndOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, stack_end_)); + } + + static ThreadOffset JniEnvOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, jni_env_)); + } + + static ThreadOffset TopOfManagedStackOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, managed_stack_) + + ManagedStack::TopQuickFrameOffset()); + } + + static ThreadOffset TopOfManagedStackPcOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, managed_stack_) + + ManagedStack::TopQuickFramePcOffset()); + } + + const ManagedStack* GetManagedStack() const { + return &managed_stack_; + } + + // Linked list recording fragments of managed stack. + void PushManagedStackFragment(ManagedStack* fragment) { + managed_stack_.PushManagedStackFragment(fragment); + } + void PopManagedStackFragment(const ManagedStack& fragment) { + managed_stack_.PopManagedStackFragment(fragment); + } + + ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame) { + return managed_stack_.PushShadowFrame(new_top_frame); + } + + ShadowFrame* PopShadowFrame() { + return managed_stack_.PopShadowFrame(); + } + + static ThreadOffset TopShadowFrameOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, managed_stack_) + + ManagedStack::TopShadowFrameOffset()); + } + + // Number of references allocated in JNI ShadowFrames on this thread + size_t NumJniShadowFrameReferences() const { + return managed_stack_.NumJniShadowFrameReferences(); + } + + // Number of references in SIRTs on this thread + size_t NumSirtReferences(); + + // Number of references allocated in SIRTs & JNI shadow frames on this thread + size_t NumStackReferences() { + return NumSirtReferences() + NumJniShadowFrameReferences(); + }; + + // Is the given obj in this thread's stack indirect reference table? + bool SirtContains(jobject obj) const; + + void SirtVisitRoots(RootVisitor* visitor, void* arg); + + void PushSirt(StackIndirectReferenceTable* sirt) { + sirt->SetLink(top_sirt_); + top_sirt_ = sirt; + } + + StackIndirectReferenceTable* PopSirt() { + StackIndirectReferenceTable* sirt = top_sirt_; + DCHECK(sirt != NULL); + top_sirt_ = top_sirt_->GetLink(); + return sirt; + } + + static ThreadOffset TopSirtOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_)); + } + + DebugInvokeReq* GetInvokeReq() { + return debug_invoke_req_; + } + + void SetDeoptimizationShadowFrame(ShadowFrame* sf); + void SetDeoptimizationReturnValue(const JValue& ret_val); + + ShadowFrame* GetAndClearDeoptimizationShadowFrame(JValue* ret_val); + + std::deque<instrumentation::InstrumentationStackFrame>* GetInstrumentationStack() { + return instrumentation_stack_; + } + + BaseMutex* GetHeldMutex(LockLevel level) const { + return held_mutexes_[level]; + } + + void SetHeldMutex(LockLevel level, BaseMutex* mutex) { + held_mutexes_[level] = mutex; + } + + void RunCheckpointFunction(); + + bool ReadFlag(ThreadFlag flag) const { + return (state_and_flags_.as_struct.flags & flag) != 0; + } + + bool TestAllFlags() const { + return (state_and_flags_.as_struct.flags != 0); + } + + void AtomicSetFlag(ThreadFlag flag); + + void AtomicClearFlag(ThreadFlag flag); + + private: + // We have no control over the size of 'bool', but want our boolean fields + // to be 4-byte quantities. + typedef uint32_t bool32_t; + + explicit Thread(bool daemon); + ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_, + Locks::thread_suspend_count_lock_); + void Destroy(); + friend class ThreadList; // For ~Thread and Destroy. + + void CreatePeer(const char* name, bool as_daemon, jobject thread_group); + friend class Runtime; // For CreatePeer. + + // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and + // Dbg::Disconnected. + ThreadState SetStateUnsafe(ThreadState new_state) { + ThreadState old_state = GetState(); + state_and_flags_.as_struct.state = new_state; + return old_state; + } + friend class SignalCatcher; // For SetStateUnsafe. + friend class Dbg; // For SetStateUnsafe. + + void VerifyStackImpl() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void DumpState(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DumpStack(std::ostream& os) const + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Out-of-line conveniences for debugging in gdb. + static Thread* CurrentFromGdb(); // Like Thread::Current. + // Like Thread::Dump(std::cerr). + void DumpFromGdb() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void* CreateCallback(void* arg); + + void HandleUncaughtExceptions(ScopedObjectAccess& soa) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void RemoveFromThreadGroup(ScopedObjectAccess& soa) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void Init(ThreadList*, JavaVMExt*) EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_); + void InitCardTable(); + void InitCpu(); + void InitFunctionPointers(); + void InitTid(); + void InitPthreadKeySelf(); + void InitStackHwm(); + + void NotifyLocked(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(wait_mutex_); + + static void ThreadExitCallback(void* arg); + + // Has Thread::Startup been called? + static bool is_started_; + + // TLS key used to retrieve the Thread*. + static pthread_key_t pthread_key_self_; + + // Used to notify threads that they should attempt to resume, they will suspend again if + // their suspend count is > 0. + static ConditionVariable* resume_cond_ GUARDED_BY(Locks::thread_suspend_count_lock_); + + // --- Frequently accessed fields first for short offsets --- + + // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to + // change from being Suspended to Runnable without a suspend request occurring. + union StateAndFlags { + struct PACKED(4) { + // Bitfield of flag values. Must be changed atomically so that flag values aren't lost. See + // ThreadFlags for bit field meanings. + volatile uint16_t flags; + // Holds the ThreadState. May be changed non-atomically between Suspended (ie not Runnable) + // transitions. Changing to Runnable requires that the suspend_request be part of the atomic + // operation. If a thread is suspended and a suspend_request is present, a thread may not + // change to Runnable as a GC or other operation is in progress. + volatile uint16_t state; + } as_struct; + volatile int32_t as_int; + }; + union StateAndFlags state_and_flags_; + COMPILE_ASSERT(sizeof(union StateAndFlags) == sizeof(int32_t), + sizeof_state_and_flags_and_int32_are_different); + + // A non-zero value is used to tell the current thread to enter a safe point + // at the next poll. + int suspend_count_ GUARDED_BY(Locks::thread_suspend_count_lock_); + + // The biased card table, see CardTable for details + byte* card_table_; + + // The pending exception or NULL. + mirror::Throwable* exception_; + + // The end of this thread's stack. This is the lowest safely-addressable address on the stack. + // We leave extra space so there's room for the code that throws StackOverflowError. + byte* stack_end_; + + // The top of the managed stack often manipulated directly by compiler generated code. + ManagedStack managed_stack_; + + // Every thread may have an associated JNI environment + JNIEnvExt* jni_env_; + + // Initialized to "this". On certain architectures (such as x86) reading + // off of Thread::Current is easy but getting the address of Thread::Current + // is hard. This field can be read off of Thread::Current to give the address. + Thread* self_; + + // Our managed peer (an instance of java.lang.Thread). The jobject version is used during thread + // start up, until the thread is registered and the local opeer_ is used. + mirror::Object* opeer_; + jobject jpeer_; + + // The "lowest addressable byte" of the stack + byte* stack_begin_; + + // Size of the stack + size_t stack_size_; + + // Thin lock thread id. This is a small integer used by the thin lock implementation. + // This is not to be confused with the native thread's tid, nor is it the value returned + // by java.lang.Thread.getId --- this is a distinct value, used only for locking. One + // important difference between this id and the ids visible to managed code is that these + // ones get reused (to ensure that they fit in the number of bits available). + uint32_t thin_lock_id_; + + // System thread id. + pid_t tid_; + + ThrowLocation throw_location_; + + // Guards the 'interrupted_' and 'wait_monitor_' members. + mutable Mutex* wait_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER; + ConditionVariable* wait_cond_ GUARDED_BY(wait_mutex_); + // Pointer to the monitor lock we're currently waiting on (or NULL). + Monitor* wait_monitor_ GUARDED_BY(wait_mutex_); + // Thread "interrupted" status; stays raised until queried or thrown. + bool32_t interrupted_ GUARDED_BY(wait_mutex_); + // The next thread in the wait set this thread is part of. + Thread* wait_next_; + // If we're blocked in MonitorEnter, this is the object we're trying to lock. + mirror::Object* monitor_enter_object_; + + friend class Monitor; + friend class MonitorInfo; + + // Top of linked list of stack indirect reference tables or NULL for none + StackIndirectReferenceTable* top_sirt_; + + Runtime* runtime_; + + RuntimeStats stats_; + + // Needed to get the right ClassLoader in JNI_OnLoad, but also + // useful for testing. + mirror::ClassLoader* class_loader_override_; + + // Thread local, lazily allocated, long jump context. Used to deliver exceptions. + Context* long_jump_context_; + + // A boolean telling us whether we're recursively throwing OOME. + bool32_t throwing_OutOfMemoryError_; + + // How much of 'suspend_count_' is by request of the debugger, used to set things right + // when the debugger detaches. Must be <= suspend_count_. + int debug_suspend_count_ GUARDED_BY(Locks::thread_suspend_count_lock_); + + // JDWP invoke-during-breakpoint support. + DebugInvokeReq* debug_invoke_req_; + + // Shadow frame that is used temporarily during the deoptimization of a method. + ShadowFrame* deoptimization_shadow_frame_; + JValue deoptimization_return_value_; + + // Additional stack used by method instrumentation to store method and return pc values. + // Stored as a pointer since std::deque is not PACKED. + std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack_; + + // A cached copy of the java.lang.Thread's name. + std::string* name_; + + // Is the thread a daemon? + const bool32_t daemon_; + + // A cached pthread_t for the pthread underlying this Thread*. + pthread_t pthread_self_; + + // Support for Mutex lock hierarchy bug detection. + BaseMutex* held_mutexes_[kLockLevelCount]; + + // A positive value implies we're in a region where thread suspension isn't expected. + uint32_t no_thread_suspension_; + + // Cause for last suspension. + const char* last_no_thread_suspension_cause_; + + // Pending checkpoint functions. + Closure* checkpoint_function_; + + public: + // Runtime support function pointers + // TODO: move this near the top, since changing its offset requires all oats to be recompiled! + EntryPoints entrypoints_; + + private: + // How many times has our pthread key's destructor been called? + uint32_t thread_exit_check_count_; + + friend class ScopedThreadStateChange; + + DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +std::ostream& operator<<(std::ostream& os, const Thread& thread); +std::ostream& operator<<(std::ostream& os, const ThreadState& state); + +} // namespace art + +#endif // ART_SRC_THREAD_H_ |