Add infrastructure for registering built-in native methods.
While I'm here, make fiddling with Thread state easier.
Change-Id: I3d215a3a852aa8970c3974b2edefce9dd261ccd7
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 4b56435..3d66909 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -217,8 +217,7 @@
Thread* current_thread = Thread::Current();
Thread::State old;
if (current_thread != NULL) {
- old = current_thread->GetState();
- current_thread->SetState(Thread::kNative);
+ old = current_thread->SetState(Thread::kNative);
}
UniquePtr<LockedFd> fd(LockedFd::CreateAndLock(cache_path_tmp, 0644));
if (current_thread != NULL) {
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 5acb155..8fb1618 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -500,10 +500,8 @@
LOG(INFO) << "[" << *self << " waiting for \"" << path_ << "\" "
<< "JNI_OnLoad...]";
}
- Thread::State old_state = self->GetState();
- self->SetState(Thread::kWaiting); // TODO: VMWAIT
+ ScopedThreadStateChange tsc(self, Thread::kWaiting); // TODO: VMWAIT
pthread_cond_wait(&jni_on_load_cond_, jni_on_load_lock_->GetImpl());
- self->SetState(old_state);
}
bool okay = (jni_on_load_result_ == kOkay);
@@ -2744,10 +2742,11 @@
// want to switch from RUNNING to VMWAIT while it executes. This allows
// the GC to ignore us.
Thread* self = Thread::Current();
- Thread::State old_state = self->GetState();
- self->SetState(Thread::kWaiting); // TODO: VMWAIT
- void* handle = dlopen(path.c_str(), RTLD_LAZY);
- self->SetState(old_state);
+ void* handle = NULL;
+ {
+ ScopedThreadStateChange tsc(self, Thread::kWaiting); // TODO: VMWAIT
+ handle = dlopen(path.c_str(), RTLD_LAZY);
+ }
if (verbose_jni) {
LOG(INFO) << "[Call to dlopen(\"" << path << "\") returned " << handle << "]";
@@ -2792,13 +2791,14 @@
const ClassLoader* old_class_loader = self->GetClassLoaderOverride();
self->SetClassLoaderOverride(class_loader);
- old_state = self->GetState();
- self->SetState(Thread::kNative);
- if (verbose_jni) {
- LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+ int version = 0;
+ {
+ ScopedThreadStateChange tsc(self, Thread::kNative);
+ if (verbose_jni) {
+ LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+ }
+ version = (*jni_on_load)(this, NULL);
}
- int version = (*jni_on_load)(this, NULL);
- self->SetState(old_state);
self->SetClassLoaderOverride(old_class_loader);;
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 42ab158..3e3e381 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -283,7 +283,7 @@
// Check that registering native methods is successful
{
- JNINativeMethod methods[] = {{"hashCode", "()I", reinterpret_cast<void*>(BogusMethod)}};
+ JNINativeMethod methods[] = {{"getClass", "()Ljava/lang/Class;", reinterpret_cast<void*>(BogusMethod)}};
env_->RegisterNatives(jlobject, methods, 1);
}
EXPECT_FALSE(env_->ExceptionCheck());
diff --git a/src/runtime.cc b/src/runtime.cc
index 1ac45f4..b126ad9 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -288,15 +288,7 @@
}
instance_ = runtime.release();
- // Most JNI libraries can just use System.loadLibrary, but you can't
- // if you're the library that implements System.loadLibrary!
- Thread* self = Thread::Current();
- Thread::State old_state = self->GetState();
- self->SetState(Thread::kNative);
- JniConstants::init(self->GetJniEnv());
- LoadJniLibrary(instance_->GetJavaVM(), "javacore");
- self->SetState(old_state);
-
+ instance_->InitLibraries();
instance_->signal_catcher_ = new SignalCatcher;
return instance_;
@@ -338,6 +330,52 @@
return true;
}
+void Runtime::InitLibraries() {
+ Thread* self = Thread::Current();
+ JNIEnv* env = self->GetJniEnv();
+
+ // Must be in the kNative state for JNI-based method registration.
+ ScopedThreadStateChange tsc(self, Thread::kNative);
+
+ // First set up the native methods provided by the runtime itself.
+ RegisterRuntimeNativeMethods(env);
+
+ // Now set up libcore, which is just a JNI library with a JNI_OnLoad.
+ // Most JNI libraries can just use System.loadLibrary, but you can't
+ // if you're the library that implements System.loadLibrary!
+ JniConstants::init(env);
+ LoadJniLibrary(instance_->GetJavaVM(), "javacore");
+}
+
+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_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_); // The _ avoids collision with libcore.
+ //REGISTER(register_java_lang_Thread);
+ //REGISTER(register_java_lang_Throwable);
+ //REGISTER(register_java_lang_VMClassLoader);
+ //REGISTER(register_java_lang_reflect_AccessibleObject);
+ //REGISTER(register_java_lang_reflect_Array);
+ //REGISTER(register_java_lang_reflect_Constructor);
+ //REGISTER(register_java_lang_reflect_Field);
+ //REGISTER(register_java_lang_reflect_Method);
+ //REGISTER(register_java_lang_reflect_Proxy);
+ //REGISTER(register_java_util_concurrent_atomic_AtomicLong);
+ //REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmServer);
+ //REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmVmInternal);
+ //REGISTER(register_sun_misc_Unsafe);
+#undef REGISTER
+}
+
void Runtime::DumpStatistics(std::ostream& os) {
// TODO: dump other runtime statistics?
os << "Loaded classes: " << class_linker_->NumLoadedClasses() << "\n";
diff --git a/src/runtime.h b/src/runtime.h
index 40db814..198bfeb 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -104,6 +104,8 @@
void BlockSignals();
bool Init(const Options& options, bool ignore_unrecognized);
+ void InitLibraries();
+ void RegisterRuntimeNativeMethods(JNIEnv*);
// The default stack size for managed threads created by the runtime.
size_t stack_size_;
diff --git a/src/scoped_jni_thread_state.h b/src/scoped_jni_thread_state.h
index dd39cc3..0da03d6 100644
--- a/src/scoped_jni_thread_state.h
+++ b/src/scoped_jni_thread_state.h
@@ -15,11 +15,11 @@
explicit ScopedJniThreadState(JNIEnv* env)
: env_(reinterpret_cast<JNIEnvExt*>(env)) {
self_ = ThreadForEnv(env);
- self_->SetState(Thread::kRunnable);
+ old_thread_state_ = self_->SetState(Thread::kRunnable);
}
~ScopedJniThreadState() {
- self_->SetState(Thread::kNative);
+ self_->SetState(old_thread_state_);
}
JNIEnvExt* Env() {
@@ -49,6 +49,7 @@
JNIEnvExt* env_;
Thread* self_;
+ Thread::State old_thread_state_;
DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
};
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index b83e71d..5979a0f 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -74,6 +74,24 @@
Heap::CollectGarbage();
}
+int WaitForSignal(Thread* thread, sigset_t& mask) {
+ ScopedThreadStateChange tsc(thread, Thread::kWaiting); // TODO: VMWAIT
+
+ // Signals for sigwait() must be blocked but not ignored. We
+ // block signals like SIGQUIT for all threads, so the condition
+ // is met. When the signal hits, we wake up, without any signal
+ // handlers being invoked.
+
+ // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
+ int signal_number;
+ int rc = TEMP_FAILURE_RETRY(sigwait(&mask, &signal_number));
+ if (rc != 0) {
+ PLOG(FATAL) << "sigwait failed";
+ }
+
+ return signal_number;
+}
+
void* SignalCatcher::Run(void*) {
CHECK(Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true));
Thread* self = Thread::Current();
@@ -88,31 +106,12 @@
sigaddset(&mask, SIGUSR1);
while (true) {
- self->SetState(Thread::kWaiting); // TODO: VMWAIT
-
- // Signals for sigwait() must be blocked but not ignored. We
- // block signals like SIGQUIT for all threads, so the condition
- // is met. When the signal hits, we wake up, without any signal
- // handlers being invoked.
-
- // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
- int signal_number;
- int rc = TEMP_FAILURE_RETRY(sigwait(&mask, &signal_number));
- if (rc != 0) {
- PLOG(FATAL) << "sigwait failed";
- }
-
- if (!halt_) {
- LOG(INFO) << *self << ": reacting to signal " << signal_number;
- }
-
- // Set our status to runnable, self-suspending if GC in progress.
- self->SetState(Thread::kRunnable);
-
+ int signal_number = WaitForSignal(self, mask);
if (halt_) {
return NULL;
}
+ LOG(INFO) << *self << ": reacting to signal " << signal_number;
switch (signal_number) {
case SIGQUIT:
HandleSigQuit();
diff --git a/src/thread.h b/src/thread.h
index 41bb0da..86e78b7 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -320,8 +320,10 @@
return state_;
}
- void SetState(State new_state) {
+ State SetState(State new_state) {
+ State old_state = state_;
state_ = new_state;
+ return old_state;
}
static ThreadOffset SuspendCountOffset() {
@@ -531,8 +533,7 @@
}
Thread::State old_state;
if (current_thread != NULL) {
- old_state = current_thread->GetState();
- current_thread->SetState(Thread::kWaiting); // TODO: VMWAIT
+ old_state = current_thread->SetState(Thread::kWaiting); // TODO: VMWAIT
} else {
// happens during VM shutdown
old_state = Thread::kUnknown; // TODO: something else
@@ -553,6 +554,22 @@
DISALLOW_COPY_AND_ASSIGN(ThreadListLock);
};
+class ScopedThreadStateChange {
+ public:
+ ScopedThreadStateChange(Thread* thread, Thread::State new_state) : thread_(thread) {
+ old_thread_state_ = thread_->SetState(new_state);
+ }
+
+ ~ScopedThreadStateChange() {
+ thread_->SetState(old_thread_state_);
+ }
+
+ private:
+ Thread* thread_;
+ Thread::State old_thread_state_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedThreadStateChange);
+};
+
} // namespace art
#endif // ART_SRC_THREAD_H_