• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
18 #define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
19 
20 #include "jni.h"
21 
22 #include "base/locks.h"
23 #include "base/macros.h"
24 #include "base/value_object.h"
25 #include "thread_state.h"
26 
27 namespace art {
28 
29 class JavaVMExt;
30 class JNIEnvExt;
31 template<class MirrorType> class ObjPtr;
32 class Thread;
33 
34 namespace mirror {
35 class Object;
36 }  // namespace mirror
37 
38 // Scoped change into and out of a particular state. Handles Runnable transitions that require
39 // more complicated suspension checking. The subclasses ScopedObjectAccessUnchecked and
40 // ScopedObjectAccess are used to handle the change into Runnable to Get direct access to objects,
41 // the unchecked variant doesn't aid annotalysis.
42 class ScopedThreadStateChange : public ValueObject {
43  public:
44   ALWAYS_INLINE ScopedThreadStateChange(Thread* self, ThreadState new_thread_state)
45       REQUIRES(!Locks::thread_suspend_count_lock_);
46 
47   ALWAYS_INLINE ~ScopedThreadStateChange() REQUIRES(!Locks::thread_suspend_count_lock_);
48 
Self()49   ALWAYS_INLINE Thread* Self() const {
50     return self_;
51   }
52 
53  protected:
54   // Constructor used by ScopedJniThreadState for an unattached thread that has access to the VM*.
ScopedThreadStateChange()55   ScopedThreadStateChange() {}
56 
57   Thread* const self_ = nullptr;
58   const ThreadState thread_state_ = kTerminated;
59 
60  private:
61   ThreadState old_thread_state_ = kTerminated;
62   const bool expected_has_no_thread_ = true;
63 
64   friend class ScopedObjectAccessUnchecked;
65   DISALLOW_COPY_AND_ASSIGN(ScopedThreadStateChange);
66 };
67 
68 // Assumes we are already runnable.
69 class ScopedObjectAccessAlreadyRunnable : public ValueObject {
70  public:
Self()71   Thread* Self() const {
72     return self_;
73   }
74 
Env()75   JNIEnvExt* Env() const {
76     return env_;
77   }
78 
Vm()79   JavaVMExt* Vm() const {
80     return vm_;
81   }
82 
83   bool ForceCopy() const;
84 
85   /*
86    * Add a local reference for an object to the indirect reference table associated with the
87    * current stack frame.  When the native function returns, the reference will be discarded.
88    *
89    * We need to allow the same reference to be added multiple times, and cope with nullptr.
90    *
91    * This will be called on otherwise unreferenced objects. We cannot do GC allocations here, and
92    * it's best if we don't grab a mutex.
93    */
94   template<typename T>
95   T AddLocalReference(ObjPtr<mirror::Object> obj) const
96       REQUIRES_SHARED(Locks::mutator_lock_);
97 
98   template<typename T>
99   ObjPtr<T> Decode(jobject obj) const REQUIRES_SHARED(Locks::mutator_lock_);
100 
101   ALWAYS_INLINE bool IsRunnable() const;
102 
103  protected:
104   ALWAYS_INLINE explicit ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
105       REQUIRES(!Locks::thread_suspend_count_lock_);
106 
107   ALWAYS_INLINE explicit ScopedObjectAccessAlreadyRunnable(Thread* self)
108       REQUIRES(!Locks::thread_suspend_count_lock_);
109 
110   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
111   // change into Runnable or acquire a share on the mutator_lock_.
112   // Note: The reinterpret_cast is backed by a static_assert in the cc file. Avoid a down_cast,
113   //       as it prevents forward declaration of JavaVMExt.
ScopedObjectAccessAlreadyRunnable(JavaVM * vm)114   explicit ScopedObjectAccessAlreadyRunnable(JavaVM* vm)
115       : self_(nullptr), env_(nullptr), vm_(reinterpret_cast<JavaVMExt*>(vm)) {}
116 
117   // Here purely to force inlining.
~ScopedObjectAccessAlreadyRunnable()118   ALWAYS_INLINE ~ScopedObjectAccessAlreadyRunnable() {}
119 
120   static void DCheckObjIsNotClearedJniWeakGlobal(ObjPtr<mirror::Object> obj)
121       REQUIRES_SHARED(Locks::mutator_lock_);
122 
123   // Self thread, can be null.
124   Thread* const self_;
125   // The full JNIEnv.
126   JNIEnvExt* const env_;
127   // The full JavaVM.
128   JavaVMExt* const vm_;
129 };
130 
131 // Entry/exit processing for transitions from Native to Runnable (ie within JNI functions).
132 //
133 // This class performs the necessary thread state switching to and from Runnable and lets us
134 // amortize the cost of working out the current thread. Additionally it lets us check (and repair)
135 // apps that are using a JNIEnv on the wrong thread. The class also decodes and encodes Objects
136 // into jobjects via methods of this class. Performing this here enforces the Runnable thread state
137 // for use of Object, thereby inhibiting the Object being modified by GC whilst native or VM code
138 // is also manipulating the Object.
139 //
140 // The destructor transitions back to the previous thread state, typically Native. In this state
141 // GC and thread suspension may occur.
142 //
143 // For annotalysis the subclass ScopedObjectAccess (below) makes it explicit that a shared of
144 // the mutator_lock_ will be acquired on construction.
145 class ScopedObjectAccessUnchecked : public ScopedObjectAccessAlreadyRunnable {
146  public:
147   ALWAYS_INLINE explicit ScopedObjectAccessUnchecked(JNIEnv* env)
148       REQUIRES(!Locks::thread_suspend_count_lock_);
149 
150   ALWAYS_INLINE explicit ScopedObjectAccessUnchecked(Thread* self)
151       REQUIRES(!Locks::thread_suspend_count_lock_);
152 
~ScopedObjectAccessUnchecked()153   ALWAYS_INLINE ~ScopedObjectAccessUnchecked() REQUIRES(!Locks::thread_suspend_count_lock_) {}
154 
155   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
156   // change into Runnable or acquire a share on the mutator_lock_.
ScopedObjectAccessUnchecked(JavaVM * vm)157   explicit ScopedObjectAccessUnchecked(JavaVM* vm) ALWAYS_INLINE
158       : ScopedObjectAccessAlreadyRunnable(vm), tsc_() {}
159 
160  private:
161   // The scoped thread state change makes sure that we are runnable and restores the thread state
162   // in the destructor.
163   const ScopedThreadStateChange tsc_;
164 
165   DISALLOW_COPY_AND_ASSIGN(ScopedObjectAccessUnchecked);
166 };
167 
168 // Annotalysis helping variant of the above.
169 class ScopedObjectAccess : public ScopedObjectAccessUnchecked {
170  public:
171   ALWAYS_INLINE explicit ScopedObjectAccess(JNIEnv* env)
172       REQUIRES(!Locks::thread_suspend_count_lock_)
173       SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
174 
175   ALWAYS_INLINE explicit ScopedObjectAccess(Thread* self)
176       REQUIRES(!Locks::thread_suspend_count_lock_)
177       SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
178 
179   // Base class will release share of lock. Invoked after this destructor.
180   ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE;
181 
182  private:
183   // TODO: remove this constructor. It is used by check JNI's ScopedCheck to make it believe that
184   //       routines operating with just a VM are sound, they are not, but when you have just a VM
185   //       you cannot call the unsound routines.
ScopedObjectAccess(JavaVM * vm)186   explicit ScopedObjectAccess(JavaVM* vm) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
187       : ScopedObjectAccessUnchecked(vm) {}
188 
189   friend class ScopedCheck;
190   DISALLOW_COPY_AND_ASSIGN(ScopedObjectAccess);
191 };
192 
193 // Annotalysis helper for going to a suspended state from runnable.
194 class ScopedThreadSuspension : public ValueObject {
195  public:
196   ALWAYS_INLINE explicit ScopedThreadSuspension(Thread* self, ThreadState suspended_state)
197       REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_)
198       UNLOCK_FUNCTION(Locks::mutator_lock_);
199 
200   ALWAYS_INLINE ~ScopedThreadSuspension() SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
201 
202  private:
203   Thread* const self_;
204   const ThreadState suspended_state_;
205   DISALLOW_COPY_AND_ASSIGN(ScopedThreadSuspension);
206 };
207 
208 
209 }  // namespace art
210 
211 #endif  // ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
212