blob: 21ac935a0fd35cb2e547864f0a7b570dc83c4910 [file]
/*
* 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 "java_lang_Thread.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "art_field-inl.h"
#include "art_field.h"
#include "art_method.h"
#include "base/casts.h"
#include "base/utils.h"
#include "class_linker.h"
#include "class_root-inl.h"
#include "common_throws.h"
#include "handle_scope-inl.h"
#include "handle_scope.h"
#include "interpreter/shadow_frame-inl.h"
#include "interpreter/shadow_frame.h"
#include "jni.h"
#include "jni/jni_internal.h"
#include "mirror/class-alloc-inl.h"
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "mirror/object.h"
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/object_array.h"
#include "mirror/object_reference.h"
#include "monitor.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
#include "nativehelper/scoped_utf_chars.h"
#include "obj_ptr.h"
#include "runtime.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
#include "stack_reference.h"
#include "thread.h"
#include "thread_list.h"
#include "verify_object.h"
#include "well_known_classes.h"
namespace art HIDDEN {
static jobject Thread_currentThread(JNIEnv* env, jclass) {
ScopedFastNativeObjectAccess soa(env);
return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
}
static jboolean Thread_interrupted(JNIEnv* env, jclass) {
return static_cast<JNIEnvExt*>(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE;
}
static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
ScopedFastNativeObjectAccess soa(env);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(soa, java_thread);
return (thread != nullptr) ? thread->IsInterrupted() : JNI_FALSE;
}
static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size,
jboolean daemon) {
// There are sections in the zygote that forbid thread creation.
Runtime* runtime = Runtime::Current();
if (runtime->IsZygote() && runtime->IsZygoteNoThreadSection()) {
jclass internal_error = env->FindClass("java/lang/InternalError");
CHECK(internal_error != nullptr);
env->ThrowNew(internal_error, "Cannot create threads in zygote");
return;
}
Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}
static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean has_been_started) {
// Ordinals from Java's Thread.State.
const jint kJavaNew = 0;
const jint kJavaRunnable = 1;
const jint kJavaBlocked = 2;
const jint kJavaWaiting = 3;
const jint kJavaTimedWaiting = 4;
const jint kJavaTerminated = 5;
ScopedObjectAccess soa(env);
ThreadState internal_thread_state =
(has_been_started ? ThreadState::kTerminated : ThreadState::kStarting);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(soa, java_thread);
if (thread != nullptr) {
internal_thread_state = thread->GetState();
}
switch (internal_thread_state) {
case ThreadState::kTerminated: return kJavaTerminated;
case ThreadState::kRunnable: return kJavaRunnable;
case ThreadState::kObsoleteRunnable: break; // Obsolete value.
case ThreadState::kTimedWaiting: return kJavaTimedWaiting;
case ThreadState::kSleeping: return kJavaTimedWaiting;
case ThreadState::kBlocked: return kJavaBlocked;
case ThreadState::kWaiting: return kJavaWaiting;
case ThreadState::kStarting: return kJavaNew;
case ThreadState::kNative: return kJavaRunnable;
case ThreadState::kWaitingForTaskProcessor: return kJavaWaiting;
case ThreadState::kWaitingForLockInflation: return kJavaWaiting;
case ThreadState::kWaitingForGcToComplete: return kJavaWaiting;
case ThreadState::kWaitingPerformingGc: return kJavaWaiting;
case ThreadState::kWaitingForCheckPointsToRun: return kJavaWaiting;
case ThreadState::kWaitingForDebuggerSend: return kJavaWaiting;
case ThreadState::kWaitingForDebuggerToAttach: return kJavaWaiting;
case ThreadState::kWaitingInMainDebuggerLoop: return kJavaWaiting;
case ThreadState::kWaitingForDebuggerSuspension: return kJavaWaiting;
case ThreadState::kWaitingForDeoptimization: return kJavaWaiting;
case ThreadState::kWaitingForGetObjectsAllocated: return kJavaWaiting;
case ThreadState::kWaitingForJniOnLoad: return kJavaWaiting;
case ThreadState::kWaitingForSignalCatcherOutput: return kJavaWaiting;
case ThreadState::kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
case ThreadState::kWaitingForMethodTracingStart: return kJavaWaiting;
case ThreadState::kWaitingForVisitObjects: return kJavaWaiting;
case ThreadState::kWaitingWeakGcRootRead: return kJavaRunnable;
case ThreadState::kWaitingForGcThreadFlip: return kJavaWaiting;
case ThreadState::kNativeForAbort: return kJavaWaiting;
case ThreadState::kSuspended: return kJavaRunnable;
case ThreadState::kInvalidState: break;
// Don't add a 'default' here so the compiler can spot incompatible enum changes.
}
LOG(ERROR) << "Unexpected thread state: " << internal_thread_state;
return -1; // Unreachable.
}
static jboolean Thread_holdsLock(JNIEnv* env, jclass, jobject java_object) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> object = soa.Decode<mirror::Object>(java_object);
if (object == nullptr) {
ThrowNullPointerException("object == null");
return JNI_FALSE;
}
Thread* thread = soa.Self();
return thread->HoldsLock(object);
}
static void Thread_interrupt0(JNIEnv* env, jobject java_thread) {
ScopedFastNativeObjectAccess soa(env);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(soa, java_thread);
if (thread != nullptr) {
thread->Interrupt(soa.Self());
}
}
static void Thread_setNativeName(JNIEnv* env, jobject peer, jstring java_name) {
ScopedUtfChars name(env, java_name);
{
ScopedObjectAccess soa(env);
if (soa.Decode<mirror::Object>(peer) == soa.Self()->GetPeer()) {
soa.Self()->SetThreadName(name.c_str());
return;
}
}
// Suspend thread to avoid it from killing itself while we set its name. We don't just hold the
// thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
// in the DDMS send code.
ThreadList* thread_list = Runtime::Current()->GetThreadList();
// Take suspend thread lock to avoid races with threads trying to suspend this one.
Thread* thread = thread_list->SuspendThreadByPeer(peer, SuspendReason::kInternal);
if (thread != nullptr) {
{
ScopedObjectAccess soa(env);
thread->SetThreadName(name.c_str());
}
bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
DCHECK(resumed);
}
}
/*
* Change Linux niceness priority for the given thread, returning errno.
*/
static int Thread_setNiceness0([[maybe_unused]] JNIEnv* env, jobject java_thread, jint new_niceness)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(self, self->DecodeJObject(java_thread));
if (thread != nullptr) {
return thread->SetNativeNiceness(new_niceness);
}
return 0;
}
/*
* Alter the priority of the specified thread. "new_priority" will range
* from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
* threads at Thread.NORM_PRIORITY (5). Returns corresponding niceness.
*/
static void Thread_setPriority0([[maybe_unused]] JNIEnv* env,
jobject java_thread,
jint new_priority,
jint new_niceness) REQUIRES_SHARED(art::Locks::mutator_lock_) {
// We should just do the conversion and call the above. But that would bypass the
// Android S workaround in SetNativePriority. So we have a separate code path.
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(self, self->DecodeJObject(java_thread));
if (thread != nullptr) {
thread->SetNativePriority(new_priority, new_niceness);
}
}
static int Thread_priorityForNiceness(int niceness) {
return (Thread::NicenessToPriority(niceness));
}
static int Thread_nicenessForPriority(int priority) {
return (Thread::PriorityToNiceness(priority));
}
static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> lock = soa.Decode<mirror::Object>(java_lock);
Monitor::Wait(Thread::Current(), lock.Ptr(), ms, ns, true, ThreadState::kSleeping);
}
/*
* 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
*/
static void Thread_yield(JNIEnv*, jobject) {
sched_yield();
}
enum PinningReason {
kNoReason = 0,
kNativeMethod = 1,
kMonitor = 2,
kUnsupportedFrame = 3,
};
struct VirtualThreadParkingVisitor final : public StackVisitor {
explicit VirtualThreadParkingVisitor(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames, true),
shadow_frame_count_(0),
reason_(kNoReason) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ShadowFrame* shadow_frame = GetCurrentShadowFrame();
if (shadow_frame == nullptr) {
// Stack walking continues only if the only non-interpreted frame is
// Thread.parkVirtualInternal until JIT and AOT frame is supported.
ArtMethod** quick_frame = GetCurrentQuickFrame();
ArtMethod* method = quick_frame != nullptr ? *quick_frame : nullptr;
if (method != nullptr && method->IsNative()) {
if (method == WellKnownClasses::java_lang_Thread_parkVirtualInternal) {
return true;
}
reason_ = kNativeMethod;
return false;
}
reason_ = kUnsupportedFrame;
return false;
}
if (!shadow_frame->GetLockCountData().IsEmpty()) {
reason_ = kMonitor;
return false;
}
// If the verifier was able to verify the locks are balanced, the interpreter won't update the
// lock cound data. We need to walk the stack to find the locks here. Or should we just have an
// increment/decrement counter?
Monitor::VisitLocks(this, LockVisitingCallback, this);
if (reason_ == kMonitor) {
return false;
}
DCHECK(reason_ == kNoReason);
shadow_frame_count_++;
shadow_frames_.push_back(shadow_frame);
return true;
}
static void LockVisitingCallback(ObjPtr<mirror::Object> obj, void* visitor) {
DCHECK(!obj.IsNull());
reinterpret_cast<VirtualThreadParkingVisitor*>(visitor)->reason_ = kMonitor;
}
std::vector<ShadowFrame*> shadow_frames_;
size_t shadow_frame_count_;
PinningReason reason_;
};
static void Thread_parkVirtualInternal(
JNIEnv* env, jobject, jobject v_context, jobject parked_states, jobject vm_error) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(env);
StackHandleScope<4> hs(soa.Self());
ClassLinker* cl = Runtime::Current()->GetClassLinker();
auto v_context_h = hs.NewHandle(soa.Decode<mirror::Object>(v_context));
auto parked_states_h = hs.NewHandle(soa.Decode<mirror::Object>(parked_states));
auto opeer_h = hs.NewHandle(self->GetPeer());
VirtualThreadParkingVisitor dump_visitor(self);
dump_visitor.WalkStack();
DCHECK_NE(dump_visitor.reason_, kUnsupportedFrame) << "JIT / AOT frame isn't supported.";
if (dump_visitor.reason_ != kNoReason) {
WellKnownClasses::dalvik_system_VirtualThreadContext_pinnedCarrierThread->SetObject<false>(
v_context_h.Get(), opeer_h.Get());
// Return to the java code to park the carrier thread
return;
}
size_t num_frames = dump_visitor.shadow_frames_.size();
DCHECK(!Runtime::Current()->IsActiveTransaction());
Runtime* runtime = Runtime::Current();
auto frames_h = hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
self,
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_VirtualThreadFrame__array),
num_frames));
for (size_t i = 0; i < num_frames; i++) {
const ShadowFrame* sf = dump_visitor.shadow_frames_[i];
DCHECK(sf != nullptr);
sf->CheckConsistentVRegs();
size_t num_vergs = sf->NumberOfVRegs();
int32_t non_vref_size = ShadowFrame::ComputeSizeWithoutReferences(num_vergs);
StackHandleScope<4> hs2(soa.Self());
auto vtf = hs2.NewHandle(WellKnownClasses::dalvik_system_VirtualThreadFrame.Get()->Alloc(
self, Runtime::Current()->GetHeap()->GetCurrentAllocator()));
auto frame_bytes = hs2.NewHandle(mirror::ByteArray::Alloc(self, non_vref_size));
auto declaring_class = hs2.NewHandle(sf->GetMethod()->GetDeclaringClass());
auto refs = hs2.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
self, GetClassRoot<mirror::ObjectArray<mirror::Object>>(), num_vergs));
DCHECK(!vtf.IsNull());
DCHECK(!frame_bytes.IsNull());
DCHECK(!refs.IsNull());
bool areRefsAllNull = true;
for (uint32_t j = 0; j < sf->NumberOfVRegs(); j++) {
ObjPtr<mirror::Object> obj = sf->GetVRegReference(j);
if (obj != nullptr) {
refs->Set(j, obj);
areRefsAllNull = false;
}
}
if (!areRefsAllNull) {
WellKnownClasses::dalvik_system_VirtualThreadFrame_refs->SetObject<false>(vtf.Get(),
refs.Get());
}
frame_bytes->Memcpy(0, reinterpret_cast<const int8_t*>(sf), 0, non_vref_size);
WellKnownClasses::dalvik_system_VirtualThreadFrame_frame->SetObject<false>(vtf.Get(),
frame_bytes.Get());
WellKnownClasses::dalvik_system_VirtualThreadFrame_declaringClass->SetObject<false>(
vtf.Get(), declaring_class.Get());
frames_h->Set(i, vtf.Get());
}
if (self->IsExceptionPending()) {
// It's likely a OOME. Let's throw it at the java level.
// Consider handling it in another way, e.g. pinning virtual thread, in the future.
return;
}
WellKnownClasses::dalvik_system_VirtualThreadParkedStates_frames->SetObject<false>(
parked_states_h.Get(), frames_h.Get());
WellKnownClasses::dalvik_system_VirtualThreadContext_parkedStates->SetObject<false>(
v_context_h.Get(), parked_states_h.Get());
self->SetVirtualThreadFlags(VirtualThreadFlag::kParking, true);
// Throw a VirtualThreadParkingError to unwind the stack and park the virtual thread.
env->Throw(reinterpret_cast<jthrowable>(vm_error));
}
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"),
FAST_NATIVE_METHOD(Thread, interrupted, "()Z"),
FAST_NATIVE_METHOD(Thread, isInterrupted, "()Z"),
NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"),
CRITICAL_NATIVE_METHOD(Thread, nicenessForPriority, "(I)I"),
NATIVE_METHOD(Thread, holdsLock, "(Ljava/lang/Object;)Z"),
FAST_NATIVE_METHOD(Thread, interrupt0, "()V"),
CRITICAL_NATIVE_METHOD(Thread, priorityForNiceness, "(I)I"),
NATIVE_METHOD(Thread, setNativeName, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Thread, setNiceness0, "(I)I"),
NATIVE_METHOD(Thread, setPriority0, "(II)V"),
FAST_NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"),
NATIVE_METHOD(Thread, yield, "()V"),
NATIVE_METHOD(Thread,
parkVirtualInternal,
"(Ldalvik/system/VirtualThreadContext;Ldalvik/system/"
"VirtualThreadParkedStates;Ldalvik/system/VirtualThreadParkingError;)V"),
};
void register_java_lang_Thread(JNIEnv* env) {
REGISTER_NATIVE_METHODS("java/lang/Thread");
}
} // namespace art