text: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You 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 <assert.h>
  #include "jni.h"
  #include "testframe.h"
  #include "thread_unit_test_utils.h"
  #include "jthread.h"
  #include "open/hythread.h"
  #include "open/hythread_ext.h"
  #include "ti_thread.h"
  #include "thread_manager.h"
  
  /*
   * Utilities for thread manager unit tests 
   */
  
  tested_thread_sturct_t dummy_tts_struct;
  tested_thread_sturct_t * dummy_tts = &dummy_tts_struct;
  tested_thread_sturct_t tested_threads[MAX_TESTED_THREAD_NUMBER];
  JavaVM * GLOBAL_VM = NULL;
  
  void sleep_a_click(void){
      hythread_sleep(CLICK_TIME_MSEC);
  }
  
  jthread new_jobject_thread(JNIEnv * jni_env) {
      const char * name = "<init>";
      const char * sig = "()V";
      jmethodID constructor = NULL;
      jclass thread_class;
      
      thread_class = (*jni_env)->FindClass(jni_env, "java/lang/Thread");
      constructor = (*jni_env)->GetMethodID(jni_env, thread_class, name, sig);
      return (*jni_env)->NewObject(jni_env, thread_class, constructor);
  }
  
  jobject new_jobject_thread_death(JNIEnv * jni_env) {
      const char * name = "<init>";
      const char * sig = "()V";
      jmethodID constructor = NULL;
      jclass thread_death_class;
      
      thread_death_class = (*jni_env)->FindClass(jni_env, "java/lang/ThreadDeath");
      constructor = (*jni_env)->GetMethodID(jni_env, thread_death_class, name, sig);
      return (*jni_env)->NewObject(jni_env, thread_death_class, constructor);
  }
  
  jthread new_jobject()
  {
      _jobject *jobj;
      _jjobject *jjobj;
  
      jobj = (_jobject *)calloc(1, sizeof(_jobject));
      jjobj = (_jjobject *)calloc(1, sizeof(_jjobject));
      assert(jobj);
      assert(jjobj);
      jobj->object = jjobj;
      jobj->object->data = NULL;
      jobj->object->daemon = 0;
      jobj->object->name = NULL;
      return jobj;
  }
  
  void delete_jobject(jobject obj){
  }
  
  void test_java_thread_setup(int argc, char *argv[]) {
      create_java_vm_func CreateJavaVM;
      JavaVMInitArgs args;
      JNIEnv * jni_env;
      int i;
  
      CreateJavaVM = test_get_java_vm_ptr();
      assert(CreateJavaVM);
  
      args.version = JNI_VERSION_1_2;
      args.nOptions = argc;
      args.options = (JavaVMOption *) malloc(args.nOptions * sizeof(JavaVMOption));
      args.options[0].optionString = "-Djava.class.path=.";
      for (i = 1; i < argc; i++) {
          args.options[i].optionString = argv[i];
          args.options[i].extraInfo = NULL;
      }
  
      log_debug("test_java_thread_init()");
  
      hythread_sleep(1000);
      CreateJavaVM(&GLOBAL_VM, &jni_env, &args);
  }
  
  void test_java_thread_teardown(void) {
  
      IDATA status;
  
      log_debug("test_java_thread_shutdown()");
      //hythread_detach(NULL);
  
      // status = tm_shutdown(); ??????????????? fix me: 
      // second testcase don't work after tm_shutdown() call
      status = TM_ERROR_NONE; 
          
      /*
       * not required, if there are running threads tm will exit with
       * error TM_ERROR_RUNNING_THREADS
       */
      if(!(status == TM_ERROR_NONE || status == TM_ERROR_RUNNING_THREADS)){
          log_error("test_java_thread_shutdown() FAILED: %d", status);
      }
  }
  
  void tested_threads_init(int mode){
          
      tested_thread_sturct_t *tts;
      jobject monitor;
      jrawMonitorID raw_monitor;
      JNIEnv * jni_env;
      IDATA status; 
      int i;
      
      jni_env = jthread_get_JNI_env(jthread_self());
          
      if (mode != TTS_INIT_DIFFERENT_MONITORS){
          monitor = new_jobject();
          status = jthread_monitor_init(monitor);
          tf_assert_same_v(status, TM_ERROR_NONE);
      }
      status = jthread_raw_monitor_create(&raw_monitor);
      tf_assert_same_v(status, TM_ERROR_NONE);
  
      reset_tested_thread_iterator(&tts);
      for (i = 0; i < MAX_TESTED_THREAD_NUMBER; i++){
          tts = &tested_threads[i];
          tts->my_index = i;
          tts->java_thread = new_jobject_thread(jni_env);
          tts->native_thread = NULL;
          tts->jvmti_start_proc_arg = &tts->jvmti_start_proc_arg;
          hysem_create(&tts->started, 0, 1);
          hysem_create(&tts->running, 0, 1);
          hysem_create(&tts->stop_request, 0, 1);
          hysem_create(&tts->ended, 0, 1);
          tts->phase = TT_PHASE_NONE;
          if (mode == TTS_INIT_DIFFERENT_MONITORS){
              monitor = new_jobject();
              status = jthread_monitor_init(monitor);
              tf_assert_same_v(status, TM_ERROR_NONE);
          }
          tts->monitor = monitor;
          tts->raw_monitor = raw_monitor;
          tts->excn = NULL;
      }
  }
  
  //void tested_threads_shutdown(void){
  //      
  //      tested_thread_sturct_t *tts;
  //      int i;
  //      
  //      reset_tested_thread_iterator(&tts);
  //      for (i = 0; i < MAX_TESTED_THREAD_NUMBER; i++){
  //              tts = &tested_threads[i];
  //        delete_jobject(tts->java_thread);
  //        delete_JNIEnv(tts->jni_env);
  //      }
  //}
  
  tested_thread_sturct_t *get_tts(int tts_index){
      
      if (tts_index >= 0 && tts_index < MAX_TESTED_THREAD_NUMBER){
          return &tested_threads[tts_index];
      }
      return (void *)NULL;
  }
  
  int next_tested_thread(tested_thread_sturct_t **tts_ptr){
  
      tested_thread_sturct_t *tts = *tts_ptr;
      int tts_index;
  
      if (! tts){
          tts = &tested_threads[0];
      } else {
          tts_index = tts->my_index;
          if (tts_index >= 0 && tts_index < MAX_TESTED_THREAD_NUMBER - 1){
              tts = &tested_threads[tts_index + 1];
          } else {
              tts = NULL;
          }
      }
      *tts_ptr = tts;
      return tts != NULL;
  }
  
  int prev_tested_thread(tested_thread_sturct_t **tts_ptr){
      
      tested_thread_sturct_t *tts = *tts_ptr;
      int tts_index;
  
      if (! tts){
          tts = &tested_threads[MAX_TESTED_THREAD_NUMBER - 1];
      } else {
          tts_index = tts->my_index;
          if (tts_index > 0 && tts_index < MAX_TESTED_THREAD_NUMBER){
              tts = &tested_threads[tts_index - 1];
          } else {
              tts = NULL;
          }
      }
      *tts_ptr = tts;
      return tts != NULL;
  }
  
  void reset_tested_thread_iterator(tested_thread_sturct_t **tts){
      *tts = (void *)NULL;
  }
  
  void tested_threads_run_common(jvmtiStartFunction run_method_param){
      tested_thread_sturct_t *tts;
      JNIEnv * jni_env = jthread_get_JNI_env(jthread_self());
  
      reset_tested_thread_iterator(&tts);
      while(next_tested_thread(&tts)){
          tts->attrs.proc = run_method_param;
          tts->attrs.arg = tts;
          tf_assert_same_v(jthread_create_with_function(jni_env, tts->java_thread, &tts->attrs), TM_ERROR_NONE);
          tested_thread_wait_started(tts);
          tts->native_thread = jthread_get_native_thread(tts->java_thread);
      }
  }
  
  void tested_threads_run(jvmtiStartFunction run_method_param){
  
      tested_threads_init(TTS_INIT_COMMON_MONITOR);
      tested_threads_run_common(run_method_param);
  }
  
  void tested_threads_run_with_different_monitors(jvmtiStartFunction run_method_param){
  
      tested_threads_init(TTS_INIT_DIFFERENT_MONITORS);
      tested_threads_run_common(run_method_param);
  }
  
  void tested_os_threads_run(hythread_entrypoint_t run_method_param){
  
      tested_thread_sturct_t *tts;
      IDATA status;
  
      tested_threads_init(TTS_INIT_COMMON_MONITOR);
      reset_tested_thread_iterator(&tts);
      while(next_tested_thread(&tts)){
          // Create thread
          tts->native_thread = (hythread_t)calloc(1, sizeof(struct VM_thread));
          tf_assert_v(tts->native_thread != NULL);
          tts->native_thread->java_status = TM_STATUS_ALLOCATED;
          status = hythread_create_ex(tts->native_thread,
              NULL, 0, 0, NULL, run_method_param, tts);
          tf_assert_v(status == TM_ERROR_NONE);
          tested_thread_wait_started(tts);
      }
  }
  
  void tested_thread_started(tested_thread_sturct_t * tts) {
      hysem_set(tts->started, 1);
  }
  
  void tested_thread_ended(tested_thread_sturct_t * tts) {
      hysem_set(tts->ended, 1);
  }
  
  void tested_thread_send_stop_request(tested_thread_sturct_t * tts) {
      hysem_set(tts->stop_request, 1);    
  }
  
  void tested_thread_wait_for_stop_request(tested_thread_sturct_t * tts) {
      IDATA status;
      do {
          hysem_set(tts->running, 1);
          status = hysem_wait_timed(tts->stop_request, SLEEP_TIME, 0);
      } while (status == TM_ERROR_TIMEOUT);
  }
  
  IDATA tested_thread_wait_for_stop_request_timed(tested_thread_sturct_t * tts, I_64 sleep_time) {
      hysem_set(tts->running, 1);
      return hysem_wait_timed(tts->stop_request, sleep_time, 0);
  }
  
  int tested_threads_stop() {
  
      tested_thread_sturct_t *tts;
      
      reset_tested_thread_iterator(&tts);
      while (next_tested_thread(&tts)) {
          tested_thread_send_stop_request(tts);
      }
      while (next_tested_thread(&tts)) {
          tested_thread_wait_ended(tts);
      }
      return TEST_PASSED;
  }
  
  int tested_threads_destroy() {
  
      tested_thread_sturct_t *tts;
      
      reset_tested_thread_iterator(&tts);
      while (next_tested_thread(&tts)) {
          tested_thread_send_stop_request(tts);
      }
      reset_tested_thread_iterator(&tts);
      while (next_tested_thread(&tts)) {
          tested_thread_wait_dead(tts);
      }
  
      return TEST_PASSED;
  }
  
  
  int check_structure(tested_thread_sturct_t *tts){
  
      jthread java_thread = tts->java_thread;
      vm_thread_t vm_thread;
      jvmti_thread_t jvmti_thread;
      hythread_t hythread;
  
      vm_thread = jthread_get_vm_thread_from_java(java_thread);
      tf_assert(vm_thread);
      hythread = (hythread_t)vm_thread;
      tf_assert_same(hythread, tts->native_thread);
      jvmti_thread = &(vm_thread->jvmti_thread);
      tf_assert(jvmti_thread);
      return TEST_PASSED;
  }
  
  int check_phase(tested_thread_sturct_t *tts, int phase) {
      
      tf_assert(tts->phase != TT_PHASE_ERROR);
      if (phase == TT_PHASE_ANY) {
          tf_assert(tts->phase != TT_PHASE_NONE);
      } else if (tts->phase != phase) {
          log_info("Expected phase %d, but got %d", phase, tts->phase);
          tf_assert_same(tts->phase, phase);
      }
      return TEST_PASSED;
  }
  
  void tested_thread_wait_started(tested_thread_sturct_t *tts) {
      int i;
       
      i = 0;
      while (hysem_wait_timed(tts->started, MAX_TIME_TO_WAIT, 0) == TM_ERROR_TIMEOUT) {
          i++;
          printf("Thread %i hasn't started for %i milliseconds", 
              tts->my_index, (i * MAX_TIME_TO_WAIT));
      }
  
      hysem_post(tts->started);
  }
  
  void tested_thread_wait_running(tested_thread_sturct_t *tts) {
      int i;
       
      i = 0;
      while (hysem_wait_timed(tts->running, MAX_TIME_TO_WAIT, 0) == TM_ERROR_TIMEOUT) {
          i++;
          printf("Thread %i isn't running after %i milliseconds", 
              tts->my_index, (i * MAX_TIME_TO_WAIT));
      }    
  }
  
  void tested_thread_wait_ended(tested_thread_sturct_t *tts) {
      int i;
       
      i = 0;
      while (hysem_wait_timed(tts->ended, MAX_TIME_TO_WAIT, 0) == TM_ERROR_TIMEOUT) {
          i++;
          log_info("Thread %i hasn't ended for %i milliseconds",
              tts->my_index, (i * MAX_TIME_TO_WAIT));
      }
      hysem_post(tts->ended);
  }
  
  void tested_thread_wait_dead(tested_thread_sturct_t *tts) {
      test_thread_join(tts->native_thread, tts->my_index);
  }
  
  void test_thread_join(hythread_t native_thread, int index) {
      int i = 0;
      do {
          hythread_sleep(SLEEP_TIME);
          if(!hythread_is_alive(native_thread)) {
              break;
          }
          if (!i && (i % (MAX_TIME_TO_WAIT / SLEEP_TIME)) == 0) {
              printf("Thread %i isn't dead after %i milliseconds",
                  index, (++i * SLEEP_TIME));
          }
      } while(1);
  }
  
  int compare_threads(jthread *threads, int thread_nmb, int compare_from_end) {
  
      int i;
      int j;
      int found;
      int tested_thread_start;
      jthread java_thread;
  
      // Check that all thread_nmb threads are different
  
      //printf("----------------------------------------------- %i %i\n", thread_nmb, compare_from_end);
      //for (j = 0; j < MAX_TESTED_THREAD_NUMBER; j++){
      //      printf("[%i] %p\n",j,tested_threads[j].java_thread->object);
      //}
      //printf("\n");
      //for (i = 0; i < thread_nmb; i++){
      //      printf("!!! %p\n", (*(threads + i))->object);
      //}
      for (i = 0; i < thread_nmb - 1; i++){
          java_thread = *(threads + i);
          for (j = i + 1; j < thread_nmb; j++){
              if (*(threads + j) == java_thread){
                  return TM_ERROR_INTERNAL;
              }
          }
      }
  
      // Check that the set of threads are equal to the set of the first 
      // or the last thread_nmb tested threads
  
      tested_thread_start = compare_from_end ? MAX_TESTED_THREAD_NUMBER - thread_nmb : 0;
      for (i = 0; i < thread_nmb; i++){
          java_thread = *(threads + i);
          found = 0;
          for (j = tested_thread_start; j < thread_nmb + tested_thread_start; j++){
              if (tested_threads[j].java_thread->object == java_thread->object){
                  found = 1;
                  break;
              }
          }
          tf_assert(found);
      }
      return TM_ERROR_NONE;
  }
  
  int compare_pointer_sets(void ** set_a, void ** set_b, int nmb){
      return TEST_FAILED;
  }
  
  int check_exception(jobject excn){
  
      return TM_ERROR_INTERNAL;
      //return TM_ERROR_NONE;
  }
  
  void JNICALL default_run_for_test(jvmtiEnv * jvmti_env, JNIEnv * jni_env, void *arg) {
  
      tested_thread_sturct_t * tts = (tested_thread_sturct_t *)arg;
      
      tts->phase = TT_PHASE_RUNNING;
      tested_thread_started(tts);
      tested_thread_wait_for_stop_request(tts);
      tts->phase = TT_PHASE_DEAD;
      tested_thread_ended(tts);
  }
  
  