blob: 355da0638bd81bcab302cd57ff72ced30753afc6 [file]
/*
* 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);
}