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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
/*
* Copyright (C) 2016 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_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
#define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
#include "jni.h"
#include "base/locks.h"
#include "base/macros.h"
#include "base/value_object.h"
#include "thread_state.h"
namespace art {
class JavaVMExt;
class JNIEnvExt;
template<class MirrorType> class ObjPtr;
class Thread;
namespace mirror {
class Object;
} // namespace mirror
// Scoped change into and out of a particular state. Handles Runnable transitions that require
// more complicated suspension checking. The subclasses ScopedObjectAccessUnchecked and
// ScopedObjectAccess are used to handle the change into Runnable to Get direct access to objects,
// the unchecked variant doesn't aid annotalysis.
class ScopedThreadStateChange : public ValueObject {
public:
ALWAYS_INLINE ScopedThreadStateChange(Thread* self, ThreadState new_thread_state)
REQUIRES(!Locks::thread_suspend_count_lock_);
ALWAYS_INLINE ~ScopedThreadStateChange() REQUIRES(!Locks::thread_suspend_count_lock_);
ALWAYS_INLINE Thread* Self() const {
return self_;
}
protected:
// Constructor used by ScopedJniThreadState for an unattached thread that has access to the VM*.
ScopedThreadStateChange() {}
Thread* const self_ = nullptr;
const ThreadState thread_state_ = kTerminated;
private:
void ScopedThreadChangeDestructorCheck();
ThreadState old_thread_state_ = kTerminated;
const bool expected_has_no_thread_ = true;
friend class ScopedObjectAccessUnchecked;
DISALLOW_COPY_AND_ASSIGN(ScopedThreadStateChange);
};
// Assumes we are already runnable.
class ScopedObjectAccessAlreadyRunnable : public ValueObject {
public:
Thread* Self() const {
return self_;
}
JNIEnvExt* Env() const {
return env_;
}
JavaVMExt* Vm() const {
return vm_;
}
bool ForceCopy() const;
/*
* Add a local reference for an object to the indirect reference table associated with the
* current stack frame. When the native function returns, the reference will be discarded.
*
* We need to allow the same reference to be added multiple times, and cope with nullptr.
*
* This will be called on otherwise unreferenced objects. We cannot do GC allocations here, and
* it's best if we don't grab a mutex.
*/
template<typename T>
T AddLocalReference(ObjPtr<mirror::Object> obj) const
REQUIRES_SHARED(Locks::mutator_lock_);
template<typename T>
ObjPtr<T> Decode(jobject obj) const REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE bool IsRunnable() const;
protected:
ALWAYS_INLINE explicit ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
REQUIRES(!Locks::thread_suspend_count_lock_);
ALWAYS_INLINE explicit ScopedObjectAccessAlreadyRunnable(Thread* self)
REQUIRES(!Locks::thread_suspend_count_lock_);
// Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
// change into Runnable or acquire a share on the mutator_lock_.
// Note: The reinterpret_cast is backed by a static_assert in the cc file. Avoid a down_cast,
// as it prevents forward declaration of JavaVMExt.
explicit ScopedObjectAccessAlreadyRunnable(JavaVM* vm)
: self_(nullptr), env_(nullptr), vm_(reinterpret_cast<JavaVMExt*>(vm)) {}
// Here purely to force inlining.
ALWAYS_INLINE ~ScopedObjectAccessAlreadyRunnable() {}
static void DCheckObjIsNotClearedJniWeakGlobal(ObjPtr<mirror::Object> obj)
REQUIRES_SHARED(Locks::mutator_lock_);
// Self thread, can be null.
Thread* const self_;
// The full JNIEnv.
JNIEnvExt* const env_;
// The full JavaVM.
JavaVMExt* const vm_;
};
// Entry/exit processing for transitions from Native to Runnable (ie within JNI functions).
//
// This class performs the necessary thread state switching to and from Runnable and lets us
// amortize the cost of working out the current thread. Additionally it lets us check (and repair)
// apps that are using a JNIEnv on the wrong thread. The class also decodes and encodes Objects
// into jobjects via methods of this class. Performing this here enforces the Runnable thread state
// for use of Object, thereby inhibiting the Object being modified by GC whilst native or VM code
// is also manipulating the Object.
//
// The destructor transitions back to the previous thread state, typically Native. In this state
// GC and thread suspension may occur.
//
// For annotalysis the subclass ScopedObjectAccess (below) makes it explicit that a shared of
// the mutator_lock_ will be acquired on construction.
class ScopedObjectAccessUnchecked : public ScopedObjectAccessAlreadyRunnable {
public:
ALWAYS_INLINE explicit ScopedObjectAccessUnchecked(JNIEnv* env)
REQUIRES(!Locks::thread_suspend_count_lock_);
ALWAYS_INLINE explicit ScopedObjectAccessUnchecked(Thread* self)
REQUIRES(!Locks::thread_suspend_count_lock_);
ALWAYS_INLINE ~ScopedObjectAccessUnchecked() REQUIRES(!Locks::thread_suspend_count_lock_) {}
// Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
// change into Runnable or acquire a share on the mutator_lock_.
explicit ScopedObjectAccessUnchecked(JavaVM* vm) ALWAYS_INLINE
: ScopedObjectAccessAlreadyRunnable(vm), tsc_() {}
private:
// The scoped thread state change makes sure that we are runnable and restores the thread state
// in the destructor.
const ScopedThreadStateChange tsc_;
DISALLOW_COPY_AND_ASSIGN(ScopedObjectAccessUnchecked);
};
// Annotalysis helping variant of the above.
class ScopedObjectAccess : public ScopedObjectAccessUnchecked {
public:
ALWAYS_INLINE explicit ScopedObjectAccess(JNIEnv* env)
REQUIRES(!Locks::thread_suspend_count_lock_)
SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
ALWAYS_INLINE explicit ScopedObjectAccess(Thread* self)
REQUIRES(!Locks::thread_suspend_count_lock_)
SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
// Base class will release share of lock. Invoked after this destructor.
~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE;
private:
// TODO: remove this constructor. It is used by check JNI's ScopedCheck to make it believe that
// routines operating with just a VM are sound, they are not, but when you have just a VM
// you cannot call the unsound routines.
explicit ScopedObjectAccess(JavaVM* vm) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
: ScopedObjectAccessUnchecked(vm) {}
friend class ScopedCheck;
DISALLOW_COPY_AND_ASSIGN(ScopedObjectAccess);
};
// Annotalysis helper for going to a suspended state from runnable.
class ScopedThreadSuspension : public ValueObject {
public:
ALWAYS_INLINE explicit ScopedThreadSuspension(Thread* self, ThreadState suspended_state)
REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_)
UNLOCK_FUNCTION(Locks::mutator_lock_);
ALWAYS_INLINE ~ScopedThreadSuspension() SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
private:
Thread* const self_;
const ThreadState suspended_state_;
DISALLOW_COPY_AND_ASSIGN(ScopedThreadSuspension);
};
} // namespace art
#endif // ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
|