diff options
-rw-r--r-- | build/Android.common.mk | 6 | ||||
-rw-r--r-- | src/class_linker.cc | 3 | ||||
-rw-r--r-- | src/class_linker.h | 4 | ||||
-rw-r--r-- | src/dalvik_system_VMStack.cc | 65 | ||||
-rw-r--r-- | src/heap.cc | 18 | ||||
-rw-r--r-- | src/intern_table.cc | 13 | ||||
-rw-r--r-- | src/intern_table.h | 4 | ||||
-rw-r--r-- | src/java_lang_Thread.cc | 128 | ||||
-rw-r--r-- | src/jni_internal.cc | 22 | ||||
-rw-r--r-- | src/jni_internal.h | 10 | ||||
-rw-r--r-- | src/logging.cc | 4 | ||||
-rw-r--r-- | src/logging.h | 12 | ||||
-rw-r--r-- | src/mutex.cc | 105 | ||||
-rw-r--r-- | src/mutex.h | 87 | ||||
-rw-r--r-- | src/runtime.cc | 12 | ||||
-rw-r--r-- | src/runtime.h | 2 | ||||
-rw-r--r-- | src/signal_catcher.cc | 7 | ||||
-rw-r--r-- | src/signal_catcher.h | 5 | ||||
-rw-r--r-- | src/thread.cc | 190 | ||||
-rw-r--r-- | src/thread.h | 169 | ||||
-rw-r--r-- | src/thread_android.cc | 91 | ||||
-rw-r--r-- | src/thread_linux.cc | 29 | ||||
-rw-r--r-- | src/thread_list.cc | 107 | ||||
-rw-r--r-- | src/thread_list.h | 88 | ||||
-rw-r--r-- | src/utils.cc | 9 | ||||
-rw-r--r-- | src/utils.h | 3 |
26 files changed, 825 insertions, 368 deletions
diff --git a/build/Android.common.mk b/build/Android.common.mk index 3c203d4..f248029 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -64,6 +64,7 @@ LIBART_COMMON_SRC_FILES := \ src/compiler/codegen/arm/Assemble.cc \ src/compiler/codegen/arm/LocalOptimizations.cc \ src/compiler/codegen/arm/armv7-a/Codegen.cc \ + src/dalvik_system_VMStack.cc \ src/dex_cache.cc \ src/dex_file.cc \ src/dex_instruction.cc \ @@ -79,6 +80,7 @@ LIBART_COMMON_SRC_FILES := \ src/java_lang_Runtime.cc \ src/java_lang_String.cc \ src/java_lang_System.cc \ + src/java_lang_Thread.cc \ src/java_lang_Throwable.cc \ src/java_util_concurrent_atomic_AtomicLong.cc \ src/jni_compiler.cc \ @@ -92,6 +94,7 @@ LIBART_COMMON_SRC_FILES := \ src/managed_register_x86.cc \ src/memory_region.cc \ src/mspace.c \ + src/mutex.cc \ src/object.cc \ src/object_bitmap.cc \ src/offsets.cc \ @@ -103,6 +106,7 @@ LIBART_COMMON_SRC_FILES := \ src/stringpiece.cc \ src/stringprintf.cc \ src/thread.cc \ + src/thread_list.cc \ src/utf.cc \ src/utils.cc \ src/zip_archive.cc \ @@ -112,12 +116,14 @@ LIBART_TARGET_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ src/logging_android.cc \ src/runtime_android.cc \ + src/thread_android.cc \ src/thread_arm.cc LIBART_HOST_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ src/logging_linux.cc \ src/runtime_linux.cc \ + src/thread_linux.cc \ src/thread_x86.cc LIBARTTEST_COMMON_SRC_FILES := \ diff --git a/src/class_linker.cc b/src/class_linker.cc index 8595bfc..a8281a3 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -71,7 +71,7 @@ ClassLinker* ClassLinker::Create(const std::vector<const DexFile*>& boot_class_p } ClassLinker::ClassLinker(InternTable* intern_table) - : classes_lock_(Mutex::Create("ClassLinker::Lock")), + : classes_lock_("ClassLinker lock"), class_roots_(NULL), array_interfaces_(NULL), array_iftable_(NULL), @@ -514,7 +514,6 @@ void ClassLinker::VisitRoots(Heap::RootVisitor* visitor, void* arg) const { } ClassLinker::~ClassLinker() { - delete classes_lock_; String::ResetClass(); Field::ResetClass(); Method::ResetClass(); diff --git a/src/class_linker.h b/src/class_linker.h index e9606a7..fa0c789 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -10,8 +10,8 @@ #include "dex_file.h" #include "heap.h" #include "macros.h" +#include "mutex.h" #include "object.h" -#include "thread.h" #include "unordered_map.h" #include "unordered_set.h" @@ -264,9 +264,9 @@ class ClassLinker { // multimap from a StringPiece hash code of a class descriptor to // Class* instances. Results should be compared for a matching // Class::descriptor_ and Class::class_loader_. + mutable Mutex classes_lock_; typedef std::tr1::unordered_multimap<size_t, Class*> Table; Table classes_; - Mutex* classes_lock_; // indexes into class_roots_. // needs to be kept in sync with class_roots_descriptors_. diff --git a/src/dalvik_system_VMStack.cc b/src/dalvik_system_VMStack.cc new file mode 100644 index 0000000..2a3f18b --- /dev/null +++ b/src/dalvik_system_VMStack.cc @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 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. + */ + +#include "jni_internal.h" +#include "object.h" + +#include "JniConstants.h" // Last to avoid problems with LOG redefinition. + +namespace art { + +namespace { + +jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject targetThread, jobjectArray javaSteArray) { + UNIMPLEMENTED(FATAL); + return 0; +} + +jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { + UNIMPLEMENTED(WARNING); + return NULL; +} + +jobjectArray VMStack_getClasses(JNIEnv* env, jclass, jint maxDepth) { + UNIMPLEMENTED(FATAL); + return NULL; +} + +jclass VMStack_getStackClass2(JNIEnv* env, jclass) { + UNIMPLEMENTED(FATAL); + return NULL; +} + +jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject targetThread) { + UNIMPLEMENTED(FATAL); + return NULL; +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"), + NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"), + NATIVE_METHOD(VMStack, getClasses, "(I)[Ljava/lang/Class;"), + NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"), + NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"), +}; + +} // namespace + +void register_dalvik_system_VMStack(JNIEnv* env) { + jniRegisterNativeMethods(env, "dalvik/system/VMStack", gMethods, NELEM(gMethods)); +} + +} // namespace art diff --git a/src/heap.cc b/src/heap.cc index 7f6b106..e346478 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -126,7 +126,7 @@ void Heap::Init(size_t initial_size, size_t maximum_size, // It's still to early to take a lock because there are no threads yet, // 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"); + lock_ = new Mutex("Heap lock"); } void Heap::Destroy() { @@ -176,7 +176,7 @@ void Heap::VerifyObject(const Object* obj) { #endif void Heap::VerifyObjectLocked(const Object* obj) { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); if (obj != NULL && !verify_object_disabled_) { if (!IsAligned(obj, kObjectAlignment)) { LOG(FATAL) << "Object isn't aligned: " << obj; @@ -224,7 +224,7 @@ void Heap::VerifyHeap() { void Heap::RecordAllocationLocked(Space* space, const Object* obj) { #ifndef NDEBUG if (Runtime::Current()->IsStarted()) { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); } #endif size_t size = space->AllocationSize(obj); @@ -235,7 +235,7 @@ void Heap::RecordAllocationLocked(Space* space, const Object* obj) { } void Heap::RecordFreeLocked(Space* space, const Object* obj) { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); size_t size = space->AllocationSize(obj); DCHECK_NE(size, 0u); if (size < num_bytes_allocated_) { @@ -263,7 +263,7 @@ void Heap::RecordImageAllocations(Space* space) { } Object* Heap::AllocateLocked(size_t size) { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); DCHECK(alloc_space_ != NULL); Space* space = alloc_space_; Object* obj = AllocateLocked(space, size); @@ -274,7 +274,7 @@ Object* Heap::AllocateLocked(size_t size) { } Object* Heap::AllocateLocked(Space* space, size_t size) { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); // Fail impossible allocations. TODO: collect soft references. if (size > maximum_size_) { @@ -365,7 +365,7 @@ void Heap::CollectGarbage() { } void Heap::CollectGarbageInternal() { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); // TODO: Suspend all threads { @@ -402,14 +402,14 @@ void Heap::CollectGarbageInternal() { } void Heap::WaitForConcurrentGcToComplete() { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); } // Given the current contents of the active heap, increase the allowed // heap footprint to match the target utilization ratio. This should // only be called immediately after a full garbage collection. void Heap::GrowForUtilization() { - DCHECK_LOCK_HELD(lock_); + lock_->AssertHeld(); UNIMPLEMENTED(ERROR); } diff --git a/src/intern_table.cc b/src/intern_table.cc index 8a4e87e..41c1b2a 100644 --- a/src/intern_table.cc +++ b/src/intern_table.cc @@ -7,12 +7,7 @@ namespace art { -InternTable::InternTable() { - intern_table_lock_ = Mutex::Create("InternTable::Lock"); -} - -InternTable::~InternTable() { - delete intern_table_lock_; +InternTable::InternTable() : intern_table_lock_("InternTable lock") { } size_t InternTable::Size() const { @@ -30,7 +25,7 @@ void InternTable::VisitRoots(Heap::RootVisitor* visitor, void* arg) const { } const String* InternTable::Lookup(Table& table, const String* s, uint32_t hash_code) { - // Requires the intern_table_lock_. + intern_table_lock_.AssertHeld(); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = table.find(hash_code), end = table.end(); it != end; ++it) { const String* existing_string = it->second; @@ -42,7 +37,7 @@ const String* InternTable::Lookup(Table& table, const String* s, uint32_t hash_c } const String* InternTable::Insert(Table& table, const String* s, uint32_t hash_code) { - // Requires the intern_table_lock_. + intern_table_lock_.AssertHeld(); table.insert(std::make_pair(hash_code, s)); return s; } @@ -53,7 +48,7 @@ void InternTable::RegisterStrong(const String* s) { } void InternTable::Remove(Table& table, const String* s, uint32_t hash_code) { - // Requires the intern_table_lock_. + intern_table_lock_.AssertHeld(); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = table.find(hash_code), end = table.end(); it != end; ++it) { if (it->second == s) { diff --git a/src/intern_table.h b/src/intern_table.h index c9de228..fe84347 100644 --- a/src/intern_table.h +++ b/src/intern_table.h @@ -6,6 +6,7 @@ #include "unordered_map.h" #include "heap.h" +#include "mutex.h" #include "object.h" namespace art { @@ -23,7 +24,6 @@ namespace art { class InternTable { public: InternTable(); - ~InternTable(); // Interns a potentially new string in the 'strong' table. (See above.) const String* InternStrong(int32_t utf16_length, const char* utf8_data); @@ -59,9 +59,9 @@ class InternTable { const String* Insert(Table& table, const String* s, uint32_t hash_code); void Remove(Table& table, const String* s, uint32_t hash_code); + mutable Mutex intern_table_lock_; Table strong_interns_; Table weak_interns_; - Mutex* intern_table_lock_; }; } // namespace art diff --git a/src/java_lang_Thread.cc b/src/java_lang_Thread.cc new file mode 100644 index 0000000..44578ee --- /dev/null +++ b/src/java_lang_Thread.cc @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2008 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. + */ + +#include "jni_internal.h" +#include "object.h" +#include "thread.h" +#include "thread_list.h" + +#include "JniConstants.h" // Last to avoid problems with LOG redefinition. + +namespace art { + +namespace { + +jobject Thread_currentThread(JNIEnv* env, jclass) { + Object* peer = Decode<Object*>(env, Thread::Current()->GetPeer()); + return AddLocalReference<jobject>(env, peer); +} + +jboolean Thread_interrupted(JNIEnv* env, jclass) { + return Thread::Current()->Interrupted(); +} + +jboolean Thread_isInterrupted(JNIEnv* env, jobject javaThread) { + ThreadListLock lock; + Thread* thread = Thread::FromManagedThread(env, javaThread); + return thread->IsInterrupted(); +} + +void Thread_nativeCreate(JNIEnv* env, jclass, jobject javaThread, jlong stackSize) { + UNIMPLEMENTED(FATAL); + //Object* threadObj = dvmDecodeIndirectRef(env, javaThread); + //dvmCreateInterpThread(threadObj, (int) stackSize); +} + +jint Thread_nativeGetStatus(JNIEnv* env, jobject javaThread) { + ThreadListLock lock; + Thread* thread = Thread::FromManagedThread(env, javaThread); + return static_cast<jint>(thread->GetState()); +} + +jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject javaThread, jobject javaObject) { + ThreadListLock lock; + //Thread* thread = Thread::FromManagedThread(env, javaThread); + //Object* object = dvmDecodeIndirectRef(env, javaObject); + //if (object == NULL) { + //dvmThrowNullPointerException("object == null"); + //return JNI_FALSE; + //} + //Thread* thread = Thread::FromManagedThread(env, javaThread); + //int result = dvmHoldsLock(thread, object); + //return result; + UNIMPLEMENTED(FATAL); + return JNI_FALSE; +} + +void Thread_nativeInterrupt(JNIEnv* env, jobject javaThread) { + ThreadListLock lock; + UNIMPLEMENTED(FATAL); + //Thread* thread = Thread::FromManagedThread(env, javaThread); + //if (thread != NULL) { + //dvmThreadInterrupt(thread); + //} +} + +void Thread_nativeSetName(JNIEnv* env, jobject javaThread, jstring javaName) { + ThreadListLock lock; + UNIMPLEMENTED(WARNING); + //Thread* thread = Thread::FromManagedThread(env, javaThread); + //StringObject* nameStr = (StringObject*) dvmDecodeIndirectRef(env, javaName); + //int threadId = (thread != NULL) ? thread->threadId : -1; + //dvmDdmSendThreadNameChange(threadId, nameStr); +} + +/* + * Alter the priority of the specified thread. "newPriority" will range + * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal" + * threads at Thread.NORM_PRIORITY (5). + */ +void Thread_nativeSetPriority(JNIEnv* env, jobject javaThread, jint newPriority) { + ThreadListLock lock; + Thread* thread = Thread::FromManagedThread(env, javaThread); + thread->SetNativePriority(newPriority); +} + +/* + * Causes the thread to temporarily pause and allow other threads to execute. + * + * The exact behavior is poorly defined. Some discussion here: + * http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html + */ +void Thread_yield(JNIEnv*, jobject) { + sched_yield(); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"), + NATIVE_METHOD(Thread, interrupted, "()Z"), + NATIVE_METHOD(Thread, isInterrupted, "()Z"), + NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;J)V"), + NATIVE_METHOD(Thread, nativeGetStatus, "()I"), + NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Thread, nativeInterrupt, "()V"), + NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"), + NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"), + NATIVE_METHOD(Thread, yield, "()V"), +}; + +} // namespace + +void register_java_lang_Thread(JNIEnv* env) { + jniRegisterNativeMethods(env, "java/lang/Thread", gMethods, NELEM(gMethods)); +} + +} // namespace art diff --git a/src/jni_internal.cc b/src/jni_internal.cc index c60dc24..83a147f 100644 --- a/src/jni_internal.cc +++ b/src/jni_internal.cc @@ -452,16 +452,12 @@ class SharedLibrary { SharedLibrary(const std::string& path, void* handle, Object* class_loader) : path_(path), handle_(handle), - jni_on_load_lock_(Mutex::Create("JNI_OnLoad lock")), + jni_on_load_lock_("JNI_OnLoad lock"), jni_on_load_thread_id_(Thread::Current()->GetThinLockId()), jni_on_load_result_(kPending) { pthread_cond_init(&jni_on_load_cond_, NULL); } - ~SharedLibrary() { - delete jni_on_load_lock_; - } - Object* GetClassLoader() { return class_loader_; } @@ -491,7 +487,7 @@ class SharedLibrary { << "JNI_OnLoad...]"; } ScopedThreadStateChange tsc(self, Thread::kWaiting); // TODO: VMWAIT - pthread_cond_wait(&jni_on_load_cond_, jni_on_load_lock_->GetImpl()); + pthread_cond_wait(&jni_on_load_cond_, jni_on_load_lock_.GetImpl()); } bool okay = (jni_on_load_result_ == kOkay); @@ -532,7 +528,7 @@ class SharedLibrary { Object* class_loader_; // Guards remaining items. - Mutex* jni_on_load_lock_; + Mutex jni_on_load_lock_; // Wait for JNI_OnLoad in other thread. pthread_cond_t jni_on_load_cond_; // Recursive invocation guard. @@ -2654,13 +2650,13 @@ JavaVMExt::JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options) log_third_party_jni(options->IsVerbose("third-party-jni")), trace(options->jni_trace_), work_around_app_jni_bugs(false), // TODO: add a way to enable this - pins_lock(Mutex::Create("JNI pin table lock")), + pins_lock("JNI pin table lock"), pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize), - globals_lock(Mutex::Create("JNI global reference table lock")), + globals_lock("JNI global reference table lock"), globals(kGlobalsInitial, kGlobalsMax, kGlobal), - weak_globals_lock(Mutex::Create("JNI weak global reference table lock")), + weak_globals_lock("JNI weak global reference table lock"), weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal), - libraries_lock(Mutex::Create("JNI shared libraries map lock")), + libraries_lock("JNI shared libraries map lock"), libraries(new Libraries) { functions = unchecked_functions = &gInvokeInterface; if (check_jni) { @@ -2669,10 +2665,6 @@ JavaVMExt::JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options) } JavaVMExt::~JavaVMExt() { - delete pins_lock; - delete globals_lock; - delete weak_globals_lock; - delete libraries_lock; delete libraries; } diff --git a/src/jni_internal.h b/src/jni_internal.h index a0fe8fc..a723b32 100644 --- a/src/jni_internal.h +++ b/src/jni_internal.h @@ -8,6 +8,7 @@ #include "heap.h" #include "indirect_reference_table.h" #include "macros.h" +#include "mutex.h" #include "reference_table.h" #include "runtime.h" @@ -18,7 +19,6 @@ namespace art { class ClassLoader; class Libraries; class Method; -class Mutex; class Thread; void JniAbort(const char* jni_function_name); @@ -64,18 +64,18 @@ struct JavaVMExt : public JavaVM { bool work_around_app_jni_bugs; // Used to hold references to pinned primitive arrays. - Mutex* pins_lock; + Mutex pins_lock; ReferenceTable pin_table; // JNI global references. - Mutex* globals_lock; + Mutex globals_lock; IndirectReferenceTable globals; // JNI weak global references. - Mutex* weak_globals_lock; + Mutex weak_globals_lock; IndirectReferenceTable weak_globals; - Mutex* libraries_lock; + Mutex libraries_lock; Libraries* libraries; // Used by -Xcheck:jni. diff --git a/src/logging.cc b/src/logging.cc index 03788c3..5e9ae3a 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -22,8 +22,8 @@ namespace { -art::Mutex* GetLoggingLock() { - static art::Mutex* lock = art::Mutex::Create("LogMessage lock"); +art::Mutex& GetLoggingLock() { + static art::Mutex lock("LogMessage lock"); return lock; } diff --git a/src/logging.h b/src/logging.h index 66190ac..43596b5 100644 --- a/src/logging.h +++ b/src/logging.h @@ -72,20 +72,8 @@ #define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2) #define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2) - // These require "utils.h" and only work with bionic (not glibc). -#ifdef __BIONIC__ -#define DCHECK_LOCK_HELD(l) CHECK_EQ(art::GetOwner(l->GetImpl()), art::GetTid()) -#define DCHECK_LOCK_NOT_HELD(l) CHECK_NE(art::GetOwner(l->GetImpl()), art::GetTid()) -#else -#define DCHECK_LOCK_HELD(l) -#define DCHECK_LOCK_NOT_HELD(l) -#endif - #else // NDEBUG -#define DCHECK_LOCK_HELD(l) -#define DCHECK_LOCK_NOT_HELD(l) - #define DCHECK(condition) \ while (false) \ CHECK(condition) diff --git a/src/mutex.cc b/src/mutex.cc new file mode 100644 index 0000000..4dba6b7 --- /dev/null +++ b/src/mutex.cc @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#include "mutex.h" + +#include <errno.h> + +#include "logging.h" +#include "utils.h" + +namespace art { + +Mutex::Mutex(const char* name) : name_(name) { +#ifndef NDEBUG + pthread_mutexattr_t debug_attributes; + errno = pthread_mutexattr_init(&debug_attributes); + if (errno != 0) { + PLOG(FATAL) << "pthread_mutexattr_init failed"; + } +#if VERIFY_OBJECT_ENABLED + errno = pthread_mutexattr_settype(&debug_attributes, PTHREAD_MUTEX_RECURSIVE); +#else + errno = pthread_mutexattr_settype(&debug_attributes, PTHREAD_MUTEX_ERRORCHECK); +#endif + if (errno != 0) { + PLOG(FATAL) << "pthread_mutexattr_settype failed"; + } + errno = pthread_mutex_init(&mutex_, &debug_attributes); + if (errno != 0) { + PLOG(FATAL) << "pthread_mutex_init failed"; + } + errno = pthread_mutexattr_destroy(&debug_attributes); + if (errno != 0) { + PLOG(FATAL) << "pthread_mutexattr_destroy failed"; + } +#else + errno = pthread_mutex_init(&mutex_, NULL); + if (errno != 0) { + PLOG(FATAL) << "pthread_mutex_init failed"; + } +#endif +} + +Mutex::~Mutex() { + errno = pthread_mutex_destroy(&mutex_); + if (errno != 0) { + PLOG(FATAL) << "pthread_mutex_destroy failed"; + } +} + +void Mutex::Lock() { + int result = pthread_mutex_lock(&mutex_); + if (result != 0) { + errno = result; + PLOG(FATAL) << "pthread_mutex_lock failed"; + } +} + +bool Mutex::TryLock() { + int result = pthread_mutex_trylock(&mutex_); + if (result == EBUSY) { + return false; + } + if (result != 0) { + errno = result; + PLOG(FATAL) << "pthread_mutex_trylock failed"; + } + return true; +} + +void Mutex::Unlock() { + int result = pthread_mutex_unlock(&mutex_); + if (result != 0) { + errno = result; + PLOG(FATAL) << "pthread_mutex_unlock failed"; + } +} + +pid_t Mutex::GetOwner() { +#ifdef __BIONIC__ + return static_cast<pid_t>((mutex_.value >> 16) & 0xffff); +#else + UNIMPLEMENTED(FATAL); + return 0; +#endif +} + +pid_t Mutex::GetTid() { + return art::GetTid(); +} + +} // namespace diff --git a/src/mutex.h b/src/mutex.h new file mode 100644 index 0000000..cbfd5a0 --- /dev/null +++ b/src/mutex.h @@ -0,0 +1,87 @@ +/* + * 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_MUTEX_H_ +#define ART_SRC_MUTEX_H_ + +#include <pthread.h> +#include <string> + +#include "logging.h" +#include "macros.h" + +namespace art { + +class Mutex { + public: + explicit Mutex(const char* name); + ~Mutex(); + + void Lock(); + + bool TryLock(); + + void Unlock(); + + const char* GetName() { + return name_.c_str(); + } + + pthread_mutex_t* GetImpl() { + return &mutex_; + } + + void AssertHeld() { +#ifdef __BIONIC__ + DCHECK_EQ(GetOwner(), GetTid()); +#endif + } + + void AssertNotHeld() { +#ifdef __BIONIC__ + DCHECK_NE(GetOwner(), GetTid()); +#endif + } + + private: + pid_t GetOwner(); + pid_t GetTid(); + + std::string name_; + + pthread_mutex_t mutex_; + + DISALLOW_COPY_AND_ASSIGN(Mutex); +}; + +class MutexLock { + public: + explicit MutexLock(Mutex& mu) : mu_(mu) { + mu_.Lock(); + } + + ~MutexLock() { + mu_.Unlock(); + } + + private: + Mutex& mu_; + DISALLOW_COPY_AND_ASSIGN(MutexLock); +}; + +} // namespace art + +#endif // ART_SRC_MUTEX_H_ diff --git a/src/runtime.cc b/src/runtime.cc index 9517bc2..454a38c 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -14,6 +14,7 @@ #include "jni_internal.h" #include "signal_catcher.h" #include "thread.h" +#include "thread_list.h" // TODO: this drags in cutil/log.h, which conflicts with our logging.h. #include "JniConstants.h" @@ -363,7 +364,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { abort_ = options->hook_abort_; default_stack_size_ = options->stack_size_; - thread_list_ = ThreadList::Create(); + thread_list_ = new ThreadList; intern_table_ = new InternTable; @@ -407,18 +408,17 @@ void Runtime::InitLibraries() { void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { #define REGISTER(FN) extern void FN(JNIEnv*); FN(env) - //REGISTER(register_dalvik_bytecode_OpcodeInfo); //REGISTER(register_dalvik_system_DexFile); //REGISTER(register_dalvik_system_VMDebug); //REGISTER(register_dalvik_system_VMRuntime); - //REGISTER(register_dalvik_system_VMStack); + REGISTER(register_dalvik_system_VMStack); //REGISTER(register_dalvik_system_Zygote); //REGISTER(register_java_lang_Class); REGISTER(register_java_lang_Object); REGISTER(register_java_lang_Runtime); REGISTER(register_java_lang_String); REGISTER(register_java_lang_System); - //REGISTER(register_java_lang_Thread); + REGISTER(register_java_lang_Thread); REGISTER(register_java_lang_Throwable); //REGISTER(register_java_lang_VMClassLoader); //REGISTER(register_java_lang_reflect_AccessibleObject); @@ -434,7 +434,7 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { #undef REGISTER } -void Runtime::DumpStatistics(std::ostream& os) { +void Runtime::Dump(std::ostream& os) { // TODO: dump other runtime statistics? os << "Loaded classes: " << class_linker_->NumLoadedClasses() << "\n"; os << "Intern table size: " << GetInternTable()->Size() << "\n"; @@ -445,6 +445,8 @@ void Runtime::DumpStatistics(std::ostream& os) { // gDvm.pBootLoaderAlloc->curOffset); // LOGI("GC precise methods: %d", dvmPointerSetGetCount(gDvm.preciseMethods)); os << "\n"; + + thread_list_->Dump(os); } void Runtime::BlockSignals() { diff --git a/src/runtime.h b/src/runtime.h index 99caa29..bcb118c 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -92,7 +92,7 @@ class Runtime { // Detaches the current native thread from the runtime. void DetachCurrentThread(); - void DumpStatistics(std::ostream& os); + void Dump(std::ostream& os); ~Runtime(); diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc index 1552180..7061b36 100644 --- a/src/signal_catcher.cc +++ b/src/signal_catcher.cc @@ -29,8 +29,7 @@ namespace art { -SignalCatcher::SignalCatcher() { - lock_ = Mutex::Create("SignalCatcher lock"); +SignalCatcher::SignalCatcher() : lock_("SignalCatcher lock") { SetHaltFlag(false); // Create a raw pthread; its start routine will attach to the runtime. @@ -72,9 +71,7 @@ void SignalCatcher::HandleSigQuit() { os << "Cmd line: " << cmdline << "\n"; } - Runtime* runtime = Runtime::Current(); - runtime->DumpStatistics(os); - runtime->GetThreadList()->Dump(os); + Runtime::Current()->Dump(os); std::string maps; if (ReadFileToString("/proc/self/maps", &maps)) { diff --git a/src/signal_catcher.h b/src/signal_catcher.h index 8d91a94..dc05d06 100644 --- a/src/signal_catcher.h +++ b/src/signal_catcher.h @@ -17,11 +17,10 @@ #ifndef ART_SRC_SIGNAL_CATCHER_H_ #define ART_SRC_SIGNAL_CATCHER_H_ -#include <pthread.h> +#include "mutex.h" namespace art { -class Mutex; class Runtime; class Thread; @@ -44,7 +43,7 @@ class SignalCatcher { void SetHaltFlag(bool new_value); bool ShouldHalt(); - Mutex* lock_; + mutable Mutex lock_; bool halt_; pthread_t thread_; }; diff --git a/src/thread.cc b/src/thread.cc index 78cb1c6..4b477b8 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -17,6 +17,7 @@ #include "object.h" #include "runtime.h" #include "runtime_support.h" +#include "thread_list.h" #include "utils.h" namespace art { @@ -310,74 +311,6 @@ void Thread::InitFunctionPointers() { 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 - pthread_mutexattr_t debug_attributes; - errno = pthread_mutexattr_init(&debug_attributes); - if (errno != 0) { - PLOG(FATAL) << "pthread_mutexattr_init failed"; - } -#if VERIFY_OBJECT_ENABLED - errno = pthread_mutexattr_settype(&debug_attributes, PTHREAD_MUTEX_RECURSIVE); -#else - errno = pthread_mutexattr_settype(&debug_attributes, PTHREAD_MUTEX_ERRORCHECK); -#endif - if (errno != 0) { - PLOG(FATAL) << "pthread_mutexattr_settype failed"; - } - errno = pthread_mutex_init(&mu->mutex_, &debug_attributes); - if (errno != 0) { - PLOG(FATAL) << "pthread_mutex_init failed"; - } - errno = pthread_mutexattr_destroy(&debug_attributes); - if (errno != 0) { - PLOG(FATAL) << "pthread_mutexattr_destroy failed"; - } -#else - errno = pthread_mutex_init(&mu->mutex_, NULL); - if (errno != 0) { - PLOG(FATAL) << "pthread_mutex_init failed"; - } -#endif - return mu; -} - -void Mutex::Lock() { - int result = pthread_mutex_lock(&mutex_); - if (result != 0) { - errno = result; - PLOG(FATAL) << "pthread_mutex_lock failed"; - } -} - -bool Mutex::TryLock() { - int result = pthread_mutex_trylock(&mutex_); - if (result == EBUSY) { - return false; - } - if (result != 0) { - errno = result; - PLOG(FATAL) << "pthread_mutex_trylock failed"; - } - return true; -} - -void Mutex::Unlock() { - int result = pthread_mutex_unlock(&mutex_); - if (result != 0) { - errno = result; - PLOG(FATAL) << "pthread_mutex_unlock failed"; - } -} - void Frame::Next() { byte* next_sp = reinterpret_cast<byte*>(sp_) + GetMethod()->GetFrameSizeInBytes(); @@ -478,15 +411,14 @@ void Thread::CreatePeer(const char* name, bool as_daemon) { jobject thread_group = NULL; jobject thread_name = env->NewStringUTF(name); - jint thread_priority = 123; + jint thread_priority = GetNativePriority(); jboolean thread_is_daemon = as_daemon; jclass c = env->FindClass("java/lang/Thread"); jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); - 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); + jobject peer = env->NewObject(c, mid, thread_group, thread_name, thread_priority, thread_is_daemon); + peer_ = env->NewGlobalRef(peer); } void Thread::InitStackHwm() { @@ -631,7 +563,6 @@ void Thread::DumpState(std::ostream& os) const { int suspend_count = 0; // TODO int debug_suspend_count = 0; // TODO - void* peer_ = NULL; // TODO os << " | group=\"" << group_name << "\"" << " sCount=" << suspend_count << " dsCount=" << debug_suspend_count @@ -706,6 +637,10 @@ void Thread::Shutdown() { Thread::Thread() : peer_(NULL), + wait_mutex_("Thread wait mutex"), + wait_monitor_(NULL), + interrupted_(false), + stack_end_(NULL), top_of_managed_stack_(), native_to_managed_record_(NULL), top_sirt_(NULL), @@ -714,11 +649,8 @@ Thread::Thread() suspend_count_(0), class_loader_override_(NULL) { InitCpu(); - { - ThreadListLock mu; - thin_lock_id_ = Runtime::Current()->GetThreadList()->AllocThreadId(); - } InitFunctionPointers(); + thin_lock_id_ = Runtime::Current()->GetThreadList()->AllocThreadId(); } void MonitorExitVisitor(const Object* object, void*) { @@ -754,6 +686,10 @@ Thread::~Thread() { //dvmUnlockObject(self, lock); //lock = NULL; + // Delete our global reference to the java.lang.Thread. + jni_env_->DeleteGlobalRef(peer_); + peer_ = NULL; + delete jni_env_; jni_env_ = NULL; @@ -1057,102 +993,10 @@ std::ostream& operator<<(std::ostream& os, const Thread& thread) { << ",pthread_t=" << thread.GetImpl() << ",tid=" << thread.GetTid() << ",id=" << thread.GetThinLockId() - << ",state=" << thread.GetState() << "]"; + << ",state=" << thread.GetState() + << ",peer=" << thread.GetPeer() + << "]"; return os; } -ThreadList* ThreadList::Create() { - return new ThreadList; -} - -ThreadList::ThreadList() { - lock_ = Mutex::Create("ThreadList::Lock"); -} - -ThreadList::~ThreadList() { - if (Contains(Thread::Current())) { - Runtime::Current()->DetachCurrentThread(); - } - - // All threads should have exited and unregistered when we - // reach this point. This means that all daemon threads had been - // shutdown cleanly. - // TODO: dump ThreadList if non-empty. - CHECK_EQ(list_.size(), 0U); - - delete lock_; - lock_ = NULL; -} - -bool ThreadList::Contains(Thread* thread) { - return find(list_.begin(), list_.end(), thread) != list_.end(); -} - -void ThreadList::Dump(std::ostream& os) { - MutexLock mu(lock_); - os << "DALVIK THREADS (" << list_.size() << "):\n"; - typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto - for (It it = list_.begin(), end = list_.end(); it != end; ++it) { - (*it)->Dump(os); - os << "\n"; - } -} - -void ThreadList::Register(Thread* thread) { - //LOG(INFO) << "ThreadList::Register() " << *thread; - MutexLock mu(lock_); - CHECK(!Contains(thread)); - list_.push_back(thread); -} - -void ThreadList::Unregister() { - 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 { - MutexLock mu(lock_); - typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto - for (It it = list_.begin(), end = list_.end(); it != end; ++it) { - (*it)->VisitRoots(visitor, arg); - } -} - -uint32_t ThreadList::AllocThreadId() { - DCHECK_LOCK_HELD(lock_); - for (size_t i = 0; i < allocated_ids_.size(); ++i) { - if (!allocated_ids_[i]) { - allocated_ids_.set(i); - return i + 1; // Zero is reserved to mean "invalid". - } - } - LOG(FATAL) << "Out of internal thread ids"; - return 0; -} - -void ThreadList::ReleaseThreadId(uint32_t id) { - DCHECK_LOCK_HELD(lock_); - --id; // Zero is reserved to mean "invalid". - DCHECK(allocated_ids_[id]) << id; - allocated_ids_.reset(id); -} - -} // namespace +} // namespace art diff --git a/src/thread.h b/src/thread.h index 4d27151..3f6228a 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,12 +8,14 @@ #include <bitset> #include <iosfwd> #include <list> +#include <string> #include "dex_file.h" #include "globals.h" #include "jni_internal.h" #include "logging.h" #include "macros.h" +#include "mutex.h" #include "mem_map.h" #include "offsets.h" @@ -24,6 +26,7 @@ class Class; class ClassLinker; class ClassLoader; class Method; +class Monitor; class Object; class Runtime; class Thread; @@ -36,43 +39,6 @@ template<class T> class ObjectArray; template<class T> class PrimitiveArray; typedef PrimitiveArray<int32_t> IntArray; -class Mutex { - public: - ~Mutex(); - - void Lock(); - - bool TryLock(); - - void Unlock(); - - const char* GetName() { return name_; } - - static Mutex* Create(const char* name); - - pthread_mutex_t* GetImpl() { return &mutex_; } - - private: - explicit Mutex(const char* name) : name_(name) {} - - const char* name_; - - pthread_mutex_t mutex_; - - DISALLOW_COPY_AND_ASSIGN(Mutex); -}; - -class MutexLock { - public: - explicit MutexLock(Mutex *mu) : mu_(mu) { - mu_->Lock(); - } - ~MutexLock() { mu_->Unlock(); } - private: - Mutex* const mu_; - DISALLOW_COPY_AND_ASSIGN(MutexLock); -}; - // Stack allocated indirect reference table, allocated within the bridge frame // between managed and native code. class StackIndirectReferenceTable { @@ -155,6 +121,12 @@ class Frame { class Thread { public: + /* thread priorities, from java.lang.Thread */ + enum Priority { + kMinPriority = 1, + kNormPriority = 5, + kMaxPriority = 10, + }; enum State { kUnknown = -1, kNew, @@ -250,6 +222,13 @@ class Thread { return reinterpret_cast<Thread*>(thread); } + static Thread* FromManagedThread(JNIEnv* env, jobject thread) { + // TODO: make these more generally available, and cached. + jclass java_lang_Thread = env->FindClass("java/lang/Thread"); + jfieldID fid = env->GetFieldID(java_lang_Thread, "vmData", "I"); + return reinterpret_cast<Thread*>(static_cast<uintptr_t>(env->GetIntField(thread, fid))); + } + void Dump(std::ostream& os) const; State GetState() const { @@ -262,6 +241,22 @@ class Thread { return old_state; } + /* + * 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(); + bool CanAccessDirectReferences() const { // TODO: when we have a moving collector, we'll need: return state_ == kRunnable; return true; @@ -279,6 +274,10 @@ class Thread { return pthread_; } + jobject GetPeer() const { + return peer_; + } + // Returns the Method* for the current method. // This is used by the JNI implementation for logging and diagnostic purposes. const Method* GetCurrentMethod() const { @@ -353,6 +352,20 @@ class Thread { // Convert a jobject into a Object* Object* DecodeJObject(jobject obj); + // Implements java.lang.Thread.interrupted. + bool Interrupted() { + MutexLock mu(wait_mutex_); + bool interrupted = interrupted_; + interrupted_ = false; + return interrupted; + } + + // Implements java.lang.Thread.isInterrupted. + bool IsInterrupted() { + MutexLock mu(wait_mutex_); + return interrupted_; + } + void RegisterExceptionEntryPoint(void (*handler)(Method**)) { exception_entry_point_ = handler; } @@ -480,7 +493,14 @@ class Thread { bool is_daemon_; // Our managed peer (an instance of java.lang.Thread). - Object* peer_; + jobject peer_; + + // Guards the 'interrupted_' and 'wait_monitor_' members. + mutable Mutex wait_mutex_; + // Pointer to the monitor lock we're currently waiting on (or NULL), guarded by wait_mutex_. + Monitor* wait_monitor_; + // Thread "interrupted" status; stays raised until queried or thrown, guarded by wait_mutex_. + bool interrupted_; // FIXME: placeholder for the gc cardTable uint32_t card_table_; @@ -538,79 +558,6 @@ class Thread { std::ostream& operator<<(std::ostream& os, const Thread& thread); std::ostream& operator<<(std::ostream& os, const Thread::State& state); -class ThreadList { - public: - static const uint32_t kMaxThreadId = 0xFFFF; - static const uint32_t kInvalidId = 0; - static const uint32_t kMainId = 1; - - static ThreadList* Create(); - - ~ThreadList(); - - void Dump(std::ostream& os); - - void Register(Thread* thread); - - void Unregister(); - - bool Contains(Thread* thread); - - void VisitRoots(Heap::RootVisitor* visitor, void* arg) const; - - private: - ThreadList(); - - uint32_t AllocThreadId(); - void ReleaseThreadId(uint32_t id); - - void Lock() { - lock_->Lock(); - } - - void Unlock() { - lock_->Unlock(); - } - - Mutex* lock_; - - std::bitset<kMaxThreadId> allocated_ids_; - std::list<Thread*> list_; - - friend class Thread; - friend class ThreadListLock; - - DISALLOW_COPY_AND_ASSIGN(ThreadList); -}; - -class ThreadListLock { - public: - ThreadListLock(Thread* self = NULL) { - if (self == NULL) { - // Try to get it from TLS. - self = Thread::Current(); - } - Thread::State old_state; - if (self != NULL) { - old_state = self->SetState(Thread::kWaiting); // TODO: VMWAIT - } else { - // This happens during VM shutdown. - old_state = Thread::kUnknown; - } - Runtime::Current()->GetThreadList()->Lock(); - if (self != NULL) { - self->SetState(old_state); - } - } - - ~ThreadListLock() { - Runtime::Current()->GetThreadList()->Unlock(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(ThreadListLock); -}; - class ScopedThreadStateChange { public: ScopedThreadStateChange(Thread* thread, Thread::State new_state) : thread_(thread) { diff --git a/src/thread_android.cc b/src/thread_android.cc new file mode 100644 index 0000000..295a509 --- /dev/null +++ b/src/thread_android.cc @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#include "thread.h" + +#include <sys/time.h> +#include <sys/resource.h> +#include <limits.h> +#include <errno.h> + +#include <cutils/sched_policy.h> +#include <utils/threads.h> + +#include "macros.h" + +namespace art { + +/* + * Conversion map for "nice" values. + * + * We use Android thread priority constants to be consistent with the rest + * of the system. In some cases adjacent entries may overlap. + */ +static const int kNiceValues[10] = { + ANDROID_PRIORITY_LOWEST, /* 1 (MIN_PRIORITY) */ + ANDROID_PRIORITY_BACKGROUND + 6, + ANDROID_PRIORITY_BACKGROUND + 3, + ANDROID_PRIORITY_BACKGROUND, + ANDROID_PRIORITY_NORMAL, /* 5 (NORM_PRIORITY) */ + ANDROID_PRIORITY_NORMAL - 2, + ANDROID_PRIORITY_NORMAL - 4, + ANDROID_PRIORITY_URGENT_DISPLAY + 3, + ANDROID_PRIORITY_URGENT_DISPLAY + 2, + ANDROID_PRIORITY_URGENT_DISPLAY /* 10 (MAX_PRIORITY) */ +}; + +void Thread::SetNativePriority(int newPriority) { + if (newPriority < 1 || newPriority > 10) { + LOG(WARNING) << "bad priority " << newPriority; + newPriority = 5; + } + + int newNice = kNiceValues[newPriority-1]; + pid_t tid = GetTid(); + + if (newNice >= ANDROID_PRIORITY_BACKGROUND) { + set_sched_policy(tid, SP_BACKGROUND); + } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { + set_sched_policy(tid, SP_FOREGROUND); + } + + if (setpriority(PRIO_PROCESS, tid, newNice) != 0) { + PLOG(INFO) << *this << " setPriority(PRIO_PROCESS, " << tid << ", " << newNice << ") failed"; + } +} + +int Thread::GetNativePriority() { + errno = 0; + int native_priority = getpriority(PRIO_PROCESS, 0); + if (native_priority == -1 && errno != 0) { + PLOG(WARNING) << "getpriority failed"; + return Thread::kNormPriority; + } + + int managed_priority = Thread::kMinPriority; + for (size_t i = 0; i < arraysize(kNiceValues); i++) { + if (native_priority >= kNiceValues[i]) { + break; + } + managed_priority++; + } + if (managed_priority > Thread::kMaxPriority) { + managed_priority = Thread::kMaxPriority; + } + return managed_priority; +} + +} // namespace art diff --git a/src/thread_linux.cc b/src/thread_linux.cc new file mode 100644 index 0000000..2bbbb25 --- /dev/null +++ b/src/thread_linux.cc @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#include "thread.h" + +namespace art { + +void Thread::SetNativePriority(int) { + // Do nothing. +} + +int Thread::GetNativePriority() { + return Thread::kNormPriority; +} + +} // namespace art diff --git a/src/thread_list.cc b/src/thread_list.cc new file mode 100644 index 0000000..b0626c7 --- /dev/null +++ b/src/thread_list.cc @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#include "thread_list.h" + +namespace art { + +ThreadList::ThreadList() : lock_("ThreadList lock") { +} + +ThreadList::~ThreadList() { + if (Contains(Thread::Current())) { + Runtime::Current()->DetachCurrentThread(); + } + + // All threads should have exited and unregistered when we + // reach this point. This means that all daemon threads had been + // shutdown cleanly. + // TODO: dump ThreadList if non-empty. + CHECK_EQ(list_.size(), 0U); +} + +bool ThreadList::Contains(Thread* thread) { + return find(list_.begin(), list_.end(), thread) != list_.end(); +} + +void ThreadList::Dump(std::ostream& os) { + MutexLock mu(lock_); + os << "DALVIK THREADS (" << list_.size() << "):\n"; + typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto + for (It it = list_.begin(), end = list_.end(); it != end; ++it) { + (*it)->Dump(os); + os << "\n"; + } +} + +void ThreadList::Register(Thread* thread) { + //LOG(INFO) << "ThreadList::Register() " << *thread; + MutexLock mu(lock_); + CHECK(!Contains(thread)); + list_.push_back(thread); +} + +void ThreadList::Unregister() { + 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 { + MutexLock mu(lock_); + typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto + for (It it = list_.begin(), end = list_.end(); it != end; ++it) { + (*it)->VisitRoots(visitor, arg); + } +} + +uint32_t ThreadList::AllocThreadId() { + MutexLock mu(lock_); + for (size_t i = 0; i < allocated_ids_.size(); ++i) { + if (!allocated_ids_[i]) { + allocated_ids_.set(i); + return i + 1; // Zero is reserved to mean "invalid". + } + } + LOG(FATAL) << "Out of internal thread ids"; + return 0; +} + +void ThreadList::ReleaseThreadId(uint32_t id) { + lock_.AssertHeld(); + --id; // Zero is reserved to mean "invalid". + DCHECK(allocated_ids_[id]) << id; + allocated_ids_.reset(id); +} + +} // namespace art diff --git a/src/thread_list.h b/src/thread_list.h new file mode 100644 index 0000000..12fdde4 --- /dev/null +++ b/src/thread_list.h @@ -0,0 +1,88 @@ +/* + * 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_LIST_H_ +#define ART_SRC_THREAD_LIST_H_ + +#include "mutex.h" +#include "thread.h" + +namespace art { + +class ThreadList { + public: + static const uint32_t kMaxThreadId = 0xFFFF; + static const uint32_t kInvalidId = 0; + static const uint32_t kMainId = 1; + + ThreadList(); + ~ThreadList(); + + void Dump(std::ostream& os); + + void Register(Thread* thread); + + void Unregister(); + + bool Contains(Thread* thread); + + void VisitRoots(Heap::RootVisitor* visitor, void* arg) const; + + private: + uint32_t AllocThreadId(); + void ReleaseThreadId(uint32_t id); + + mutable Mutex lock_; + std::bitset<kMaxThreadId> allocated_ids_; + std::list<Thread*> list_; + + friend class Thread; + friend class ThreadListLock; + + DISALLOW_COPY_AND_ASSIGN(ThreadList); +}; + +class ThreadListLock { + public: + ThreadListLock(Thread* self = NULL) { + if (self == NULL) { + // Try to get it from TLS. + self = Thread::Current(); + } + Thread::State old_state; + if (self != NULL) { + old_state = self->SetState(Thread::kWaiting); // TODO: VMWAIT + } else { + // This happens during VM shutdown. + old_state = Thread::kUnknown; + } + Runtime::Current()->GetThreadList()->lock_.Lock(); + if (self != NULL) { + self->SetState(old_state); + } + } + + ~ThreadListLock() { + Runtime::Current()->GetThreadList()->lock_.Unlock(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ThreadListLock); +}; + +} // namespace art + +#endif // ART_SRC_THREAD_LIST_H_ diff --git a/src/utils.cc b/src/utils.cc index fed8a5e..d146166 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -252,15 +252,6 @@ void SetThreadName(const char *threadName) { #endif } -pid_t GetOwner(pthread_mutex_t* mutex) { -#ifdef __BIONIC__ - return static_cast<pid_t>(((mutex)->value >> 16) & 0xffff); -#else - UNIMPLEMENTED(FATAL); - return 0; -#endif -} - } // namespace art // Neither bionic nor glibc exposes gettid(2). diff --git a/src/utils.h b/src/utils.h index 135ea59..e6ed1d2 100644 --- a/src/utils.h +++ b/src/utils.h @@ -181,9 +181,6 @@ void Split(const std::string& s, char delim, std::vector<std::string>& result); // Returns the calling thread's tid. (The C libraries don't expose this.) pid_t GetTid(); -// Returns the tid of the thread that owns the given pthread mutex, or 0. -pid_t GetOwner(pthread_mutex_t* mutex); - // Sets the name of the current thread. The name may be truncated to an // implementation-defined limit. void SetThreadName(const char* name); |