Home | History | Annotate | Download | only in runtime
      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/macros.h"
     23 #include "base/mutex.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 
     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*.
     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:
     71   Thread* Self() const {
     72     return self_;
     73   }
     74 
     75   JNIEnvExt* Env() const {
     76     return env_;
     77   }
     78 
     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.
    114   explicit ScopedObjectAccessAlreadyRunnable(JavaVM* vm)
    115       : self_(nullptr), env_(nullptr), vm_(reinterpret_cast<JavaVMExt*>(vm)) {}
    116 
    117   // Here purely to force inlining.
    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 
    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_.
    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.
    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