Add a heap HWM to the Thread*, for compiled code.
Also fix a bug in thread detach, and implement the thread exit callback.
Destroy our pthread_mutex_t instances, and check for success. (This will
catch us deleting locked Mutex instances.)
Change-Id: I26cf8117b825234f6c790e0cf70b2c025a743f84
diff --git a/src/heap.cc b/src/heap.cc
index fc3fce0..d80f437 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -50,7 +50,7 @@
}
};
-bool Heap::Init(size_t initial_size, size_t maximum_size,
+void Heap::Init(size_t initial_size, size_t maximum_size,
const char* boot_image_file_name,
std::vector<const char*>& image_file_names) {
Space* boot_space;
@@ -61,8 +61,7 @@
} else {
boot_space = Space::Create(boot_image_file_name);
if (boot_space == NULL) {
- LOG(WARNING) << "Failed to create space from " << boot_image_file_name;
- return false;
+ LOG(FATAL) << "Failed to create space from " << boot_image_file_name;
}
spaces_.push_back(boot_space);
requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize);
@@ -72,8 +71,7 @@
for (size_t i = 0; i < image_file_names.size(); i++) {
Space* space = Space::Create(image_file_names[i]);
if (space == NULL) {
- LOG(WARNING) << "Failed to create space from " << image_file_names[i];
- return false;
+ LOG(FATAL) << "Failed to create space from " << image_file_names[i];
}
image_spaces.push_back(space);
spaces_.push_back(space);
@@ -82,8 +80,7 @@
Space* space = Space::Create(initial_size, maximum_size, requested_base);
if (space == NULL) {
- LOG(WARNING) << "Failed to create alloc space";
- return false;
+ LOG(FATAL) << "Failed to create alloc space";
}
if (boot_space == NULL) {
@@ -97,15 +94,13 @@
// Allocate the initial live bitmap.
UniquePtr<HeapBitmap> live_bitmap(HeapBitmap::Create(base, num_bytes));
if (live_bitmap.get() == NULL) {
- LOG(WARNING) << "Failed to create live bitmap";
- return false;
+ LOG(FATAL) << "Failed to create live bitmap";
}
// Allocate the initial mark bitmap.
UniquePtr<HeapBitmap> mark_bitmap(HeapBitmap::Create(base, num_bytes));
if (mark_bitmap.get() == NULL) {
- LOG(WARNING) << "Failed to create mark bitmap";
- return false;
+ LOG(FATAL) << "Failed to create mark bitmap";
}
alloc_space_ = space;
@@ -132,8 +127,6 @@
// but we can create the heap lock now. We don't create it earlier to
// make it clear that you can't use locks during heap initialization.
lock_ = Mutex::Create("Heap lock");
-
- return true;
}
void Heap::Destroy() {
diff --git a/src/heap.h b/src/heap.h
index 296266a..52c1f10 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -30,7 +30,7 @@
// Create a heap with the requested sizes. The optional boot image may
// be NULL, otherwise it is an image filename created by ImageWriter.
// image_file_names specifies application images to load.
- static bool Init(size_t starting_size, size_t maximum_size,
+ static void Init(size_t starting_size, size_t maximum_size,
const char* boot_image_file_name,
std::vector<const char*>& image_file_names);
diff --git a/src/runtime.cc b/src/runtime.cc
index c7ae9c1..00dd832 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -23,7 +23,7 @@
Runtime* Runtime::instance_ = NULL;
Runtime::Runtime()
- : stack_size_(0),
+ : default_stack_size_(Thread::kDefaultStackSize),
thread_list_(NULL),
intern_table_(NULL),
class_linker_(NULL),
@@ -362,27 +362,19 @@
exit_ = options->hook_exit_;
abort_ = options->hook_abort_;
- stack_size_ = options->stack_size_;
+ default_stack_size_ = options->stack_size_;
thread_list_ = ThreadList::Create();
intern_table_ = new InternTable;
- if (!Heap::Init(options->heap_initial_size_,
- options->heap_maximum_size_,
- options->boot_image_,
- options->images_)) {
- LOG(WARNING) << "Failed to create heap";
- return false;
- }
+ Heap::Init(options->heap_initial_size_, options->heap_maximum_size_,
+ options->boot_image_, options->images_);
BlockSignals();
java_vm_ = new JavaVMExt(this, options.get());
- if (!Thread::Startup()) {
- LOG(WARNING) << "Failed to startup threads";
- return false;
- }
+ Thread::Startup();
// ClassLinker needs an attached thread, but we can't fully attach a thread
// without creating objects.
diff --git a/src/runtime.h b/src/runtime.h
index 96513e6..99caa29 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -96,8 +96,8 @@
~Runtime();
- size_t GetStackSize() const {
- return stack_size_;
+ size_t GetDefaultStackSize() const {
+ return default_stack_size_;
}
ClassLinker* GetClassLinker() const {
@@ -130,7 +130,7 @@
void RegisterRuntimeNativeMethods(JNIEnv*);
// The default stack size for managed threads created by the runtime.
- size_t stack_size_;
+ size_t default_stack_size_;
ThreadList* thread_list_;
diff --git a/src/thread.cc b/src/thread.cc
index ae926ad..c771c5b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -191,6 +191,13 @@
pDebugMe = DebugMe;
}
+Mutex::~Mutex() {
+ errno = pthread_mutex_destroy(&mutex_);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_mutex_destroy failed";
+ }
+}
+
Mutex* Mutex::Create(const char* name) {
Mutex* mu = new Mutex(name);
#ifndef NDEBUG
@@ -203,7 +210,7 @@
if (errno != 0) {
PLOG(FATAL) << "pthread_mutexattr_settype failed";
}
- errno = pthread_mutex_init(&mu->lock_impl_, &debug_attributes);
+ errno = pthread_mutex_init(&mu->mutex_, &debug_attributes);
if (errno != 0) {
PLOG(FATAL) << "pthread_mutex_init failed";
}
@@ -212,7 +219,7 @@
PLOG(FATAL) << "pthread_mutexattr_destroy failed";
}
#else
- errno = pthread_mutex_init(&mu->lock_impl_, NULL);
+ errno = pthread_mutex_init(&mu->mutex_, NULL);
if (errno != 0) {
PLOG(FATAL) << "pthread_mutex_init failed";
}
@@ -221,7 +228,7 @@
}
void Mutex::Lock() {
- int result = pthread_mutex_lock(&lock_impl_);
+ int result = pthread_mutex_lock(&mutex_);
if (result != 0) {
errno = result;
PLOG(FATAL) << "pthread_mutex_lock failed";
@@ -229,7 +236,7 @@
}
bool Mutex::TryLock() {
- int result = pthread_mutex_trylock(&lock_impl_);
+ int result = pthread_mutex_trylock(&mutex_);
if (result == EBUSY) {
return false;
}
@@ -241,7 +248,7 @@
}
void Mutex::Unlock() {
- int result = pthread_mutex_unlock(&lock_impl_);
+ int result = pthread_mutex_unlock(&mutex_);
if (result != 0) {
errno = result;
PLOG(FATAL) << "pthread_mutex_unlock failed";
@@ -274,7 +281,7 @@
Thread* Thread::Create(const Runtime* runtime) {
UNIMPLEMENTED(FATAL) << "need to pass in a java.lang.Thread";
- size_t stack_size = runtime->GetStackSize();
+ size_t stack_size = runtime->GetDefaultStackSize();
Thread* new_thread = new Thread;
@@ -294,7 +301,7 @@
PLOG(FATAL) << "pthread_attr_setstacksize(" << stack_size << ") failed";
}
- errno = pthread_create(&new_thread->handle_, &attr, ThreadStart, new_thread);
+ errno = pthread_create(&new_thread->pthread_, &attr, ThreadStart, new_thread);
if (errno != 0) {
PLOG(FATAL) << "pthread_create failed";
}
@@ -314,9 +321,11 @@
Thread* self = new Thread;
self->tid_ = ::art::GetTid();
- self->handle_ = pthread_self();
+ self->pthread_ = pthread_self();
self->is_daemon_ = as_daemon;
+ self->InitStackHwm();
+
self->state_ = kRunnable;
SetThreadName(name);
@@ -350,15 +359,40 @@
jboolean thread_is_daemon = as_daemon;
jclass c = env->FindClass("java/lang/Thread");
- LOG(INFO) << "java/lang/Thread=" << (void*)c;
jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
- LOG(INFO) << "java/lang/Thread.<init>=" << (void*)mid;
jobject o = env->NewObject(c, mid, thread_group, thread_name, thread_priority, thread_is_daemon);
LOG(INFO) << "Created new java.lang.Thread " << (void*) o << " decoded=" << (void*) DecodeJObject(o);
peer_ = DecodeJObject(o);
}
+void Thread::InitStackHwm() {
+ pthread_attr_t attributes;
+ errno = pthread_getattr_np(pthread_, &attributes);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_getattr_np failed";
+ }
+
+ // stack_base is the "lowest addressable byte" of the stack.
+ void* stack_base;
+ size_t stack_size;
+ errno = pthread_attr_getstack(&attributes, &stack_base, &stack_size);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_attr_getstack failed";
+ }
+
+ const size_t kStackOverflowReservedBytes = 1024; // Space to throw a StackOverflowError in.
+ if (stack_size <= kStackOverflowReservedBytes) {
+ LOG(FATAL) << "attempt to attach a thread with a too-small stack (" << stack_size << " bytes)";
+ }
+ stack_hwm_ = reinterpret_cast<byte*>(stack_base) + stack_size - kStackOverflowReservedBytes;
+
+ errno = pthread_attr_destroy(&attributes);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_attr_destroy failed";
+ }
+}
+
void Thread::Dump(std::ostream& os) const {
/*
* Get the java.lang.Thread object. This function gets called from
@@ -437,7 +471,7 @@
int policy;
sched_param sp;
- errno = pthread_getschedparam(handle_, &policy, &sp);
+ errno = pthread_getschedparam(pthread_, &policy, &sp);
if (errno != 0) {
PLOG(FATAL) << "pthread_getschedparam failed";
}
@@ -514,27 +548,24 @@
os << "UNIMPLEMENTED: Thread::DumpStack\n";
}
-static void ThreadExitCheck(void* arg) {
- LG << "Thread exit check";
+void Thread::ThreadExitCallback(void* arg) {
+ Thread* self = reinterpret_cast<Thread*>(arg);
+ LOG(FATAL) << "Native thread exited without calling DetachCurrentThread: " << *self;
}
-bool Thread::Startup() {
+void Thread::Startup() {
// Allocate a TLS slot.
- errno = pthread_key_create(&Thread::pthread_key_self_, ThreadExitCheck);
+ errno = pthread_key_create(&Thread::pthread_key_self_, Thread::ThreadExitCallback);
if (errno != 0) {
- PLOG(WARNING) << "pthread_key_create failed";
- return false;
+ PLOG(FATAL) << "pthread_key_create failed";
}
// Double-check the TLS slot allocation.
if (pthread_getspecific(pthread_key_self_) != NULL) {
- LOG(WARNING) << "newly-created pthread TLS slot is not NULL";
- return false;
+ LOG(FATAL) << "newly-created pthread TLS slot is not NULL";
}
// TODO: initialize other locks and condition variables
-
- return true;
}
void Thread::Shutdown() {
@@ -939,21 +970,33 @@
}
void ThreadList::Register(Thread* thread) {
- LOG(INFO) << "ThreadList::Register() " << *thread;
+ //LOG(INFO) << "ThreadList::Register() " << *thread;
MutexLock mu(lock_);
CHECK(!Contains(thread));
list_.push_back(thread);
}
void ThreadList::Unregister() {
- LOG(INFO) << "ThreadList::Unregister() " << *Thread::Current();
- MutexLock mu(lock_);
Thread* self = Thread::Current();
+
+ //LOG(INFO) << "ThreadList::Unregister() " << self;
+ MutexLock mu(lock_);
+
+ // Remove this thread from the list.
CHECK(Contains(self));
list_.remove(self);
+
+ // Delete the Thread* and release the thin lock id.
uint32_t thin_lock_id = self->thin_lock_id_;
delete self;
ReleaseThreadId(thin_lock_id);
+
+ // Clear the TLS data, so that thread is recognizably detached.
+ // (It may wish to reattach later.)
+ errno = pthread_setspecific(Thread::pthread_key_self_, NULL);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_setspecific failed";
+ }
}
void ThreadList::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
diff --git a/src/thread.h b/src/thread.h
index d50b45b..b9df711 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -38,7 +38,7 @@
class Mutex {
public:
- virtual ~Mutex() {}
+ ~Mutex();
void Lock();
@@ -50,14 +50,14 @@
static Mutex* Create(const char* name);
- pthread_mutex_t* GetImpl() { return &lock_impl_; }
+ pthread_mutex_t* GetImpl() { return &mutex_; }
private:
explicit Mutex(const char* name) : name_(name) {}
const char* name_;
- pthread_mutex_t lock_impl_;
+ pthread_mutex_t mutex_;
DISALLOW_COPY_AND_ASSIGN(Mutex);
};
@@ -266,7 +266,7 @@
}
pthread_t GetImpl() const {
- return handle_;
+ return pthread_;
}
// Returns the Method* for the current method.
@@ -317,24 +317,6 @@
void* throw_pc,
const DexFile& dex_file,
ClassLinker* class_linker);
- static ThreadOffset SelfOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, self_));
- }
-
- // Offset of exception_ within Thread, used by generated code
- static ThreadOffset ExceptionOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_));
- }
-
- // Offset of thin_lock_id_ within Thread, used by generated code
- static ThreadOffset IdOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
- }
-
- // Offset of card_table within Thread, used by generated code
- static ThreadOffset CardTableOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, card_table_));
- }
void SetName(const char* name);
@@ -344,40 +326,14 @@
void Resume();
- static bool Startup();
+ static void Startup();
static void Shutdown();
- static ThreadOffset SuspendCountOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_));
- }
-
- // Offset of state within Thread, used by generated code
- static ThreadOffset StateOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, state_));
- }
-
// JNI methods
JNIEnvExt* GetJniEnv() const {
return jni_env_;
}
- // Offset of JNI environment within Thread, used by generated code
- static ThreadOffset JniEnvOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, jni_env_));
- }
-
- // Offset of top of managed stack address, used by generated code
- static ThreadOffset TopOfManagedStackOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_) +
- OFFSETOF_MEMBER(Frame, sp_));
- }
-
- // Offset of top stack indirect reference table within Thread, used by
- // generated code
- static ThreadOffset TopSirtOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
- }
-
// Number of references allocated in SIRTs on this thread
size_t NumSirtReferences();
@@ -387,20 +343,10 @@
// Convert a jobject into a Object*
Object* DecodeJObject(jobject obj);
- // Offset of exception_entry_point_ within Thread, used by generated code
- static ThreadOffset ExceptionEntryPointOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_entry_point_));
- }
-
void RegisterExceptionEntryPoint(void (*handler)(Method**)) {
exception_entry_point_ = handler;
}
- // Offset of suspend_count_entry_point_ within Thread, used by generated code
- static ThreadOffset SuspendCountEntryPointOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_entry_point_));
- }
-
void RegisterSuspendCountEntryPoint(void (*handler)(Method**)) {
suspend_count_entry_point_ = handler;
}
@@ -436,6 +382,59 @@
void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
+ //
+ // 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 IdOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
+ }
+
+ static ThreadOffset CardTableOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, card_table_));
+ }
+
+ static ThreadOffset SuspendCountOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_));
+ }
+
+ static ThreadOffset StateOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, state_));
+ }
+
+ static ThreadOffset StackHwmOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, stack_hwm_));
+ }
+
+ static ThreadOffset JniEnvOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, jni_env_));
+ }
+
+ static ThreadOffset TopOfManagedStackOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_) +
+ OFFSETOF_MEMBER(Frame, sp_));
+ }
+
+ static ThreadOffset TopSirtOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
+ }
+
+ static ThreadOffset ExceptionEntryPointOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_entry_point_));
+ }
+
+ static ThreadOffset SuspendCountEntryPointOffset() {
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_entry_point_));
+ }
+
private:
Thread();
~Thread();
@@ -449,6 +448,9 @@
void InitCpu();
void InitFunctionPointers();
+ void InitStackHwm();
+
+ static void ThreadExitCallback(void* arg);
void WalkStack(StackVisitor* visitor);
@@ -463,7 +465,7 @@
pid_t tid_;
// Native thread handle.
- pthread_t handle_;
+ pthread_t pthread_;
bool is_daemon_;
@@ -473,6 +475,9 @@
// FIXME: placeholder for the gc cardTable
uint32_t card_table_;
+ // The high water mark for this thread's stack.
+ byte* stack_hwm_;
+
// Top of the managed stack, written out prior to the state transition from
// kRunnable to kNative. Uses include to give the starting point for scanning
// a managed stack when a thread is in native code.