JNI为什么要调用AttachCurrentThread?

我们写JNI的时候,通常要通过 如果需要反调java层的代码,是需要通过jvm->AttachCurrentThread 将当前线程注册到虚拟机中,为什么一定要调用这个方法呢?我们追一下这个方法里面到底做了什么?

JNI AttachCurrentThread

  • 采用的ART debug。所有方法里面的代码均有省略,并不是全部代码
  • 我们可以在 jni.h,找到改方法的声明
1
2
3
jint AttachCurrentThread(JNIEnv **p_env, void *thr_args) {
return functions->AttachCurrentThread(this, p_env, thr_args);
}


具体实现就要转到runtime了

  • 继续找 art/runtime/check_jni.cc 的实现
1
2
3
4
5
6
7
8
9
10
static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
JniValueType result;
//这里转到
result.i = BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args);
sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
return result.i;
}
  • 继续追 BaseVm(vm)->AttachCurrentThread
  • 在 art/runtime/java_vm_ext.cc 中找到 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    static jint AttachCurrentThreadInternal(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
if (vm == nullptr || p_env == nullptr) {
return JNI_ERR;
}

// Return immediately if we're already attached.
Thread* self = Thread::Current();
if (self != nullptr) {
*p_env = self->GetJniEnv();
return JNI_OK;
}

Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->GetRuntime();

// No threads allowed in zygote mode.
if (runtime->IsZygote()) {
LOG(ERROR) << "Attempt to attach a thread in the zygote";
return JNI_ERR;
}

JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
const char* thread_name = nullptr;
jobject thread_group = nullptr;
if (args != nullptr) {
if (JavaVMExt::IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to "
<< (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
<< args->version;
return JNI_EVERSION;
}
thread_name = args->name;
thread_group = args->group;
}

//这里真实的调用了 runtime->AttachCurrentThread
if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group,
!runtime->IsAotCompiler())) {
*p_env = nullptr;
return JNI_ERR;
} else {
//如果成功
*p_env = Thread::Current()->GetJniEnv();
return JNI_OK;
}
}
};
  • 继续找 runtime->AttachCurrentThread
  • art/runtime/runtime.cc
1
2
3
4
5
6
7
8
9
10
11
12
 bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
bool create_peer) {
ScopedTrace trace(__FUNCTION__);
//实际上最终是在这里,在JVM层面包装成了一个 JVM 的 Thread,然后再返回
Thread* self = Thread::Attach(thread_name, as_daemon, thread_group, create_peer);
// Run ThreadGroup.add to notify the group that this thread is now started.
if (self != nullptr && create_peer && !IsAotCompiler()) {
ScopedObjectAccess soa(self);
self->NotifyThreadGroup(soa, thread_group);
}
return self != nullptr;
}
  • 继续再追一下 Thread::Attach的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
   Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) {
Runtime* runtime = Runtime::Current();
if (runtime == nullptr) {
LOG(ERROR) << "Thread attaching to non-existent runtime: " <<
((thread_name != nullptr) ? thread_name : "(Unnamed)");
return nullptr;
}
Thread* self;
{
MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
if (runtime->IsShuttingDownLocked()) {
LOG(WARNING) << "Thread attaching while runtime is shutting down: " <<
((thread_name != nullptr) ? thread_name : "(Unnamed)");
return nullptr;
} else {
//开始初始化
Runtime::Current()->StartThreadBirth();
self = new Thread(as_daemon);
//这里进行了初始化
bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
//结束
Runtime::Current()->EndThreadBirth();
if (!init_success) {
delete self;
return nullptr;
}
}
}

self->InitStringEntryPoints();

CHECK_NE(self->GetState(), kRunnable);
self->SetState(kNative);

// Run the action that is acting on the peer.
if (!peer_action(self)) {
runtime->GetThreadList()->Unregister(self);
// Unregister deletes self, no need to do this here.
return nullptr;
}

if (VLOG_IS_ON(threads)) {
if (thread_name != nullptr) {
VLOG(threads) << "Attaching thread " << thread_name;
} else {
VLOG(threads) << "Attaching unnamed thread.";
}
ScopedObjectAccess soa(self);
self->Dump(LOG_STREAM(INFO));
}

{
ScopedObjectAccess soa(self);
runtime->GetRuntimeCallbacks()->ThreadStart(self);
}

return self;
}
  • 转接到 init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {
//该函数执行所有必须由其应用的本机线程运行的初始化
//当我们从managed code 创建新线程时,在Thread :: Create中分配Thread *,这样可以在准备好时与相应的本机线程握手
CHECK(Thread::Current() == nullptr);

//将pthread_self_设置在pthread_setspecific之前,
tlsPtr_.pthread_self = pthread_self();
CHECK(is_started_);


//各种init
InitCpu();
InitTlsEntryPoints();
RemoveSuspendTrigger();
InitCardTable();
InitTid();
interpreter::InitInterpreterTls(this);

//各种CHECK

//把当前线程注册,实际就是push到thread_list
thread_list->Register(this);
//直接return true
return true;
}
  • 可以看到 ThreadList::Register(Thread* self)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   void ThreadList::Register(Thread* self) {


//省略不看

//以原子方式将self添加到线程列表
MutexLock mu(self, *Locks::thread_list_lock_);
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
CHECK_GE(suspend_all_count_, debug_suspend_all_count_);
for (int delta = debug_suspend_all_count_; delta > 0; delta--) {
bool updated = self->ModifySuspendCount(self, +1, nullptr, SuspendReason::kForDebugger);
DCHECK(updated);
}
for (int delta = suspend_all_count_ - debug_suspend_all_count_; delta > 0; delta--) {
bool updated = self->ModifySuspendCount(self, +1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
CHECK(!Contains(self));
//加入
list_.push_back(self);

//省略不看
}

Java层的Thread初始化,以及是怎么启动的?


我们在java层面new thread 的时候,就会在jvm层面对应一个Thread,也会做很多和native相关的初始化。Android Thread做了些改动是在线程start的时候,我们以Android的为列

1
2
3
4
5
6
// Android-changed: Use Android specific nativeCreate() method to create/start thread.
// The upstream native method start0() only takes a reference to this object and so must obtain
// the stack size and daemon status directly from the field whereas Android supplies the values
// explicitly on the method call.
// private native void start0();
private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
  • 对应到native art/runtime/native/java_lang_Thread.cc 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   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);
}
  • 重要的实现就是在CreateNativeThread里面了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
   void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();

//省略CHECK

Runtime* runtime = Runtime::Current();

//以原子方式启动线程的诞生
bool thread_start_during_shutdown = false;
{
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
if (runtime->IsShuttingDownLocked()) {
thread_start_during_shutdown = true;
} else {
//熟悉的线程初始化 StartThreadBirth(
runtime->StartThreadBirth();
// 那么end 呢????
}
}
if (thread_start_during_shutdown) {
ScopedLocalRef<jclass> error_class(env, env->FindClass("java/lang/InternalError"));
env->ThrowNew(error_class.get(), "Thread starting during runtime shutdown");
return;
}

Thread* child_thread = new Thread(is_daemon);
// Use global JNI ref to hold peer live while child thread starts.
child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer);
stack_size = FixStackSize(stack_size);

// 改变 nativePeer
// Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing
// to assign it.
env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
reinterpret_cast<jlong>(child_thread));

// Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
// do not have a good way to report this on the child's side.
std::string error_msg;
std::unique_ptr<JNIEnvExt> child_jni_env_ext(
JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM(), &error_msg));

int pthread_create_result = 0;
if (child_jni_env_ext.get() != nullptr) {
pthread_t new_pthread;
pthread_attr_t attr;
child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED),
"PTHREAD_CREATE_DETACHED");
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);

//看到没有,这里对应了一个pthread_create,是在JAVA层调用thread start创建
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,//线程启动执行的函数
child_thread);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
//创建成功
if (pthread_create_result == 0) {
// pthread_create started the new thread. The child is now responsible for managing the
// JNIEnvExt we created.
// Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization
// between the threads.
child_jni_env_ext.release();
return;
}
}
//其它创建失败的情况
}
  • Runtime::Current()->EndThreadBirth();在Thread::CreateCallback,里面 所以pthread启动后,才是EndThreadBirth()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  void* Thread::CreateCallback(void* arg) {
Thread* self = reinterpret_cast<Thread*>(arg);
Runtime* runtime = Runtime::Current();
if (runtime == nullptr) {
LOG(ERROR) << "Thread attaching to non-existent runtime: " << *self;
return nullptr;
}
{
// TODO: pass self to MutexLock - requires self to equal Thread::Current(), which is only true
// after self->Init().
MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
// Check that if we got here we cannot be shutting down (as shutdown should never have started
// while threads are being born).
CHECK(!runtime->IsShuttingDownLocked());
// Note: given that the JNIEnv is created in the parent thread, the only failure point here is
// a mess in InitStackHwm. We do not have a reasonable way to recover from that, so abort
// the runtime in such a case. In case this ever changes, we need to make sure here to
// delete the tmp_jni_env, as we own it at this point.
CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env));
self->tlsPtr_.tmp_jni_env = nullptr;
//这里end
Runtime::Current()->EndThreadBirth();
}
{
ScopedObjectAccess soa(self);
self->InitStringEntryPoints();

// Copy peer into self, deleting global reference when done.
CHECK(self->tlsPtr_.jpeer != nullptr);
self->tlsPtr_.opeer = soa.Decode<mirror::Object>(self->tlsPtr_.jpeer).Ptr();
self->GetJniEnv()->DeleteGlobalRef(self->tlsPtr_.jpeer);
self->tlsPtr_.jpeer = nullptr;
self->SetThreadName(self->GetThreadName()->ToModifiedUtf8().c_str());

ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority);
self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));

runtime->GetRuntimeCallbacks()->ThreadStart(self);

// Invoke the 'run' method of our java.lang.Thread. 真实调用thread 的 run 方法
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
}
// Detach and delete self. 这里是解注册
Runtime::Current()->GetThreadList()->Unregister(self);

return nullptr;
}

简单总结

  • 我们通过javaThread 创建线程的时候,JVM会包装成一个JVM的Thread,然后启动pthread,调用run方法,但我们通过pthread独立创建的线程,是没有和JVM里面的线程对象Thread建立关联的,JVM不认你这个线程,你独立在这个线程里面做和JVM不相关的事情是可以的,但是你要访问java,这就得需要JVM帮忙,所以需要把我们自己创建的线程warp成一个JVM层面的Thread对象,然后添加到JVM的thread list 里面去,这样在jvm层面就感知到了一个Thread的了。
  • 我们类比的话,一个先做了1然后做2 ,后一个先做了2,后面补做了1