/*
 * Copyright 2020 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.
 */

#include <atomic>
#include <sstream>

#include "base/globals.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jni.h"
#include "runtime.h"
#include "thread_list.h"

namespace art {
namespace NativeWait {

std::atomic<bool> native_waiting = false;
std::atomic<bool> native_wait = false;

// Perform late debuggable switch in the same way the zygote would (clear-jit,
// unmark-zygote, set-debuggable, deopt boot, restart jit). NB This skips
// restarting the heap threads since that doesn't seem to be needed to trigger
// b/144947842.
extern "C" JNIEXPORT void JNICALL Java_art_Test2031_simulateZygoteFork(JNIEnv*, jclass) {
  Runtime* runtime = Runtime::Current();
  bool has_jit = runtime->GetJit() != nullptr;
  if (has_jit) {
    runtime->GetJit()->PreZygoteFork();
  }
  runtime->SetAsZygoteChild(/*is_system_server=*/false, /*is_zygote=*/false);
  runtime->AddCompilerOption("--debuggable");
  runtime->SetRuntimeDebugState(Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
  {
    // Deoptimize the boot image as it may be non-debuggable.
    ScopedSuspendAll ssa(__FUNCTION__);
    runtime->DeoptimizeBootImage();
  }

  if (has_jit) {
    runtime->GetJitCodeCache()->PostForkChildAction(false, false);
    runtime->GetJit()->PostForkChildAction(false, false);
    // We have "zygote" code that isn't really part of the BCP. just don't collect it.
    runtime->GetJitCodeCache()->SetGarbageCollectCode(false);
  }
}

extern "C" JNIEXPORT void JNICALL Java_art_Test2031_setupJvmti(JNIEnv* env,
                                                               jclass,
                                                               [[maybe_unused]] jstring testdir) {
  std::ostringstream oss;
  oss << (kIsDebugBuild ? "libtiagentd.so" : "libtiagent.so")
      << "=2031-zygote-compiled-frame-deopt,art";
  LOG(INFO) << "agent " << oss.str();
  Runtime::Current()->AttachAgent(env, oss.str(), nullptr);
}
extern "C" JNIEXPORT void JNICALL Java_art_Test2031_waitForNativeSleep(JNIEnv*, jclass) {
  while (!native_waiting) {
  }
}
extern "C" JNIEXPORT void JNICALL Java_art_Test2031_wakeupNativeSleep(JNIEnv*, jclass) {
  native_wait = false;
}
extern "C" JNIEXPORT void JNICALL Java_art_Test2031_nativeSleep(JNIEnv*, jclass) {
  native_wait = true;
  do {
    native_waiting = true;
  } while (native_wait);
  native_waiting = false;
}

}  // namespace NativeWait
}  // namespace art
