text: /*
   * Copyright (C) 2012 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 "base/casts.h"
  #include "java_vm_ext.h"
  #include "jni_env_ext-inl.h"
  #include "art_field.h"
  #include "read_barrier.h"
  #include "thread-inl.h"
  #include "verify_object.h"
  
  namespace art {
  
  // 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:
    ScopedThreadStateChange(Thread* self, ThreadState new_thread_state)
        REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
        : self_(self), thread_state_(new_thread_state), expected_has_no_thread_(false) {
      if (UNLIKELY(self_ == nullptr)) {
        // Value chosen arbitrarily and won't be used in the destructor since thread_ == null.
        old_thread_state_ = kTerminated;
        Runtime* runtime = Runtime::Current();
        CHECK(runtime == nullptr || !runtime->IsStarted() || runtime->IsShuttingDown(self_));
      } else {
        DCHECK_EQ(self, Thread::Current());
        // Read state without locks, ok as state is effectively thread local and we're not interested
        // in the suspend count (this will be handled in the runnable transitions).
        old_thread_state_ = self->GetState();
        if (old_thread_state_ != new_thread_state) {
          if (new_thread_state == kRunnable) {
            self_->TransitionFromSuspendedToRunnable();
          } else if (old_thread_state_ == kRunnable) {
            self_->TransitionFromRunnableToSuspended(new_thread_state);
          } else {
            // A suspended transition to another effectively suspended transition, ok to use Unsafe.
            self_->SetState(new_thread_state);
          }
        }
      }
    }
  
    ~ScopedThreadStateChange() REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE {
      if (UNLIKELY(self_ == nullptr)) {
        if (!expected_has_no_thread_) {
          Runtime* runtime = Runtime::Current();
          bool shutting_down = (runtime == nullptr) || runtime->IsShuttingDown(nullptr);
          CHECK(shutting_down);
        }
      } else {
        if (old_thread_state_ != thread_state_) {
          if (old_thread_state_ == kRunnable) {
            self_->TransitionFromSuspendedToRunnable();
          } else if (thread_state_ == kRunnable) {
            self_->TransitionFromRunnableToSuspended(old_thread_state_);
          } else {
            // A suspended transition to another effectively suspended transition, ok to use Unsafe.
            self_->SetState(old_thread_state_);
          }
        }
      }
    }
  
    Thread* Self() const {
      return self_;
    }
  
   protected:
    // Constructor used by ScopedJniThreadState for an unattached thread that has access to the VM*.
    ScopedThreadStateChange()
        : self_(nullptr), thread_state_(kTerminated), old_thread_state_(kTerminated),
          expected_has_no_thread_(true) {}
  
    Thread* const self_;
    const ThreadState thread_state_;
  
   private:
    ThreadState old_thread_state_;
    const bool expected_has_no_thread_;
  
    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 {
      return vm_->ForceCopy();
    }
  
    /*
     * 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(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(Self());
      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
      DCHECK_NE(obj, Runtime::Current()->GetClearedJniWeakGlobal());
      return obj == nullptr ? nullptr : Env()->AddLocalReference<T>(obj);
    }
  
    template<typename T>
    T Decode(jobject obj) const
        SHARED_REQUIRES(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(Self());
      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
      return down_cast<T>(Self()->DecodeJObject(obj));
    }
  
    ArtField* DecodeField(jfieldID fid) const
        SHARED_REQUIRES(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(Self());
      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
      return reinterpret_cast<ArtField*>(fid);
    }
  
    jfieldID EncodeField(ArtField* field) const SHARED_REQUIRES(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(Self());
      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
      return reinterpret_cast<jfieldID>(field);
    }
  
    ArtMethod* DecodeMethod(jmethodID mid) const SHARED_REQUIRES(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(Self());
      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
      return reinterpret_cast<ArtMethod*>(mid);
    }
  
    jmethodID EncodeMethod(ArtMethod* method) const SHARED_REQUIRES(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(Self());
      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
      return reinterpret_cast<jmethodID>(method);
    }
  
    bool IsRunnable() const {
      return self_->GetState() == kRunnable;
    }
  
   protected:
    explicit ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
        REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
        : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
    }
  
    explicit ScopedObjectAccessAlreadyRunnable(Thread* self)
        REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
        : self_(self), env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
          vm_(env_ != nullptr ? env_->vm : nullptr) {
    }
  
    // 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 ScopedObjectAccessAlreadyRunnable(JavaVM* vm)
        : self_(nullptr), env_(nullptr), vm_(down_cast<JavaVMExt*>(vm)) {}
  
    // Here purely to force inlining.
    ~ScopedObjectAccessAlreadyRunnable() ALWAYS_INLINE {
    }
  
    // 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:
    explicit ScopedObjectAccessUnchecked(JNIEnv* env)
        REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
        : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) {
      Self()->VerifyStack();
      Locks::mutator_lock_->AssertSharedHeld(Self());
    }
  
    explicit ScopedObjectAccessUnchecked(Thread* self)
        REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
        : ScopedObjectAccessAlreadyRunnable(self), tsc_(self, kRunnable) {
      Self()->VerifyStack();
      Locks::mutator_lock_->AssertSharedHeld(Self());
    }
  
    // 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:
    explicit ScopedObjectAccess(JNIEnv* env)
        REQUIRES(!Locks::thread_suspend_count_lock_)
        SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
        : ScopedObjectAccessUnchecked(env) {
    }
  
    explicit ScopedObjectAccess(Thread* self)
        REQUIRES(!Locks::thread_suspend_count_lock_)
        SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
        : ScopedObjectAccessUnchecked(self) {
    }
  
    ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
      // Base class will release share of lock. Invoked after this destructor.
    }
  
   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:
    explicit ScopedThreadSuspension(Thread* self, ThreadState suspended_state)
        REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_)
        UNLOCK_FUNCTION(Locks::mutator_lock_)
        ALWAYS_INLINE
        : self_(self), suspended_state_(suspended_state) {
      DCHECK(self_ != nullptr);
      self_->TransitionFromRunnableToSuspended(suspended_state);
    }
  
    ~ScopedThreadSuspension() SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
      DCHECK_EQ(self_->GetState(), suspended_state_);
      self_->TransitionFromSuspendedToRunnable();
    }
  
   private:
    Thread* const self_;
    const ThreadState suspended_state_;
    DISALLOW_COPY_AND_ASSIGN(ScopedThreadSuspension);
  };
  
  
  }  // namespace art
  
  #endif  // ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
  