changeset 34255:04528c2bb8b2

Merge
author coleenp
date Mon, 23 Nov 2015 21:00:25 +0000
parents f2743650be1f 59d76c40998a
children 8f3bbb94c253
files
diffstat 13 files changed, 452 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/hotspot/make/test/JtregNative.gmk	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/make/test/JtregNative.gmk	Mon Nov 23 21:00:25 2015 +0000
@@ -51,7 +51,8 @@
 # Add conditional directories here when needed.
 ifeq ($(OPENJDK_TARGET_OS)-$(OPENJDK_TARGET_CPU_ARCH), solaris-sparc)
 BUILD_HOTSPOT_JTREG_NATIVE_SRC += \
-    $(HOTSPOT_TOPDIR)/test/runtime/libadimalloc.solaris.sparc
+    $(HOTSPOT_TOPDIR)/test/runtime/libadimalloc.solaris.sparc \
+    $(HOTSPOT_TOPDIR)/test/runtime/ThreadSignalMask
 endif
 
 ifeq ($(TOOLCHAIN_TYPE), solstudio)
--- a/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.cpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.cpp	Mon Nov 23 21:00:25 2015 +0000
@@ -499,11 +499,14 @@
   return _next_offset_threshold;
 }
 
-void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* obj_top) {
+void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* obj_top, size_t fill_size) {
   // The first BOT entry should have offset 0.
   reset_bot();
   alloc_block(_bottom, obj_top);
- }
+  if (fill_size > 0) {
+    alloc_block(obj_top, fill_size);
+  }
+}
 
 #ifndef PRODUCT
 void G1BlockOffsetArrayContigSpace::print_on(outputStream* out) {
--- a/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.hpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.hpp	Mon Nov 23 21:00:25 2015 +0000
@@ -372,7 +372,7 @@
   HeapWord* block_start_unsafe(const void* addr);
   HeapWord* block_start_unsafe_const(const void* addr) const;
 
-  void set_for_starts_humongous(HeapWord* obj_top);
+  void set_for_starts_humongous(HeapWord* obj_top, size_t fill_size);
 
   virtual void print_on(outputStream* out) PRODUCT_RETURN;
 };
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp	Mon Nov 23 21:00:25 2015 +0000
@@ -339,11 +339,18 @@
   // thread to calculate the object size incorrectly.
   Copy::fill_to_words(new_obj, oopDesc::header_size(), 0);
 
+  size_t fill_size = word_size_sum - word_size;
+  if (fill_size >= min_fill_size()) {
+    fill_with_objects(obj_top, fill_size);
+  } else {
+    fill_size = 0;
+  }
+
   // We will set up the first region as "starts humongous". This
   // will also update the BOT covering all the regions to reflect
   // that there is a single object that starts at the bottom of the
   // first region.
-  first_hr->set_starts_humongous(obj_top);
+  first_hr->set_starts_humongous(obj_top, fill_size);
   first_hr->set_allocation_context(context);
   // Then, if there are any, we will set up the "continues
   // humongous" regions.
@@ -365,9 +372,9 @@
 
   // Now that the BOT and the object header have been initialized,
   // we can update top of the "starts humongous" region.
-  first_hr->set_top(MIN2(first_hr->end(), obj_top));
+  first_hr->set_top(first_hr->end());
   if (_hr_printer.is_active()) {
-    _hr_printer.alloc(G1HRPrinter::StartsHumongous, first_hr, first_hr->top());
+    _hr_printer.alloc(G1HRPrinter::StartsHumongous, first_hr, first_hr->end());
   }
 
   // Now, we will update the top fields of the "continues humongous"
@@ -375,25 +382,18 @@
   hr = NULL;
   for (uint i = first + 1; i < last; ++i) {
     hr = region_at(i);
-    if ((i + 1) == last) {
-      // last continues humongous region
-      assert(hr->bottom() < obj_top && obj_top <= hr->end(),
-             "new_top should fall on this region");
-      hr->set_top(obj_top);
-      _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, obj_top);
-    } else {
-      // not last one
-      assert(obj_top > hr->end(), "obj_top should be above this region");
-      hr->set_top(hr->end());
+    hr->set_top(hr->end());
+    if (_hr_printer.is_active()) {
       _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, hr->end());
     }
   }
-  // If we have continues humongous regions (hr != NULL), its top should
-  // match obj_top.
-  assert(hr == NULL || (hr->top() == obj_top), "sanity");
+
+  assert(hr == NULL || (hr->bottom() < obj_top && obj_top <= hr->end()),
+         "obj_top should be in last region");
+
   check_bitmaps("Humongous Region Allocation", first_hr);
 
-  increase_used(word_size * HeapWordSize);
+  increase_used(word_size_sum * HeapWordSize);
 
   for (uint i = first; i < last; ++i) {
     _humongous_set.add(region_at(i));
--- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp	Mon Nov 23 21:00:25 2015 +0000
@@ -211,14 +211,14 @@
   _gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms;
 }
 
-void HeapRegion::set_starts_humongous(HeapWord* obj_top) {
+void HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) {
   assert(!is_humongous(), "sanity / pre-condition");
   assert(top() == bottom(), "should be empty");
 
   _type.set_starts_humongous();
   _humongous_start_region = this;
 
-  _offsets.set_for_starts_humongous(obj_top);
+  _offsets.set_for_starts_humongous(obj_top, fill_size);
 }
 
 void HeapRegion::set_continues_humongous(HeapRegion* first_hr) {
@@ -756,16 +756,6 @@
     size_t obj_size = block_size(p);
     object_num += 1;
 
-    if (is_region_humongous != g1->is_humongous(obj_size) &&
-        !g1->is_obj_dead(obj, this)) { // Dead objects may have bigger block_size since they span several objects.
-      gclog_or_tty->print_cr("obj " PTR_FORMAT " is of %shumongous size ("
-                             SIZE_FORMAT " words) in a %shumongous region",
-                             p2i(p), g1->is_humongous(obj_size) ? "" : "non-",
-                             obj_size, is_region_humongous ? "" : "non-");
-       *failures = true;
-       return;
-    }
-
     if (!g1->is_obj_dead_cond(obj, this, vo)) {
       if (obj->is_oop()) {
         Klass* klass = obj->klass();
@@ -876,14 +866,6 @@
     }
   }
 
-  if (is_region_humongous && object_num > 1) {
-    gclog_or_tty->print_cr("region [" PTR_FORMAT "," PTR_FORMAT "] is humongous "
-                           "but has " SIZE_FORMAT ", objects",
-                           p2i(bottom()), p2i(end()), object_num);
-    *failures = true;
-    return;
-  }
-
   verify_strong_code_roots(vo, failures);
 }
 
--- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp	Mon Nov 23 21:00:25 2015 +0000
@@ -455,9 +455,9 @@
   // the first region in a series of one or more contiguous regions
   // that will contain a single "humongous" object.
   //
-  // obj_top : points to the end of the humongous object that's being
-  // allocated.
-  void set_starts_humongous(HeapWord* obj_top);
+  // obj_top : points to the top of the humongous object.
+  // fill_size : size of the filler object at the end of the region series.
+  void set_starts_humongous(HeapWord* obj_top, size_t fill_size);
 
   // Makes the current region be a "continues humongous'
   // region. first_hr is the "start humongous" region of the series
--- a/hotspot/src/share/vm/logging/log.cpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/logging/log.cpp	Mon Nov 23 21:00:25 2015 +0000
@@ -37,10 +37,10 @@
 
   // Write long message to output file
   MutexLocker ml(LogConfiguration_lock);
-  LogConfiguration::parse_log_arguments("loglengthoutput.txt", "logging=develop",
+  LogConfiguration::parse_log_arguments("loglengthoutput.txt", "logging=trace",
     NULL, NULL, NULL);
   ResourceMark rm;
-  outputStream* logstream = LogHandle(logging)::develop_stream();
+  outputStream* logstream = LogHandle(logging)::trace_stream();
   logstream->print_cr("01:1234567890-"
                       "02:1234567890-"
                       "03:1234567890-"
--- a/hotspot/src/share/vm/logging/log.hpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/logging/log.hpp	Mon Nov 23 21:00:25 2015 +0000
@@ -49,11 +49,21 @@
 #define log_info(...)    (!log_is_enabled(Info, __VA_ARGS__))    ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Info>
 #define log_debug(...)   (!log_is_enabled(Debug, __VA_ARGS__))   ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Debug>
 #define log_trace(...)   (!log_is_enabled(Trace, __VA_ARGS__))   ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Trace>
+
+// Macros for logging that should be excluded in product builds.
+// Available for levels Info, Debug and Trace. Includes test macro that
+// evaluates to false in product builds.
 #ifndef PRODUCT
-#define log_develop(...) (!log_is_enabled(Develop, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Develop>
+#define log_develop_info(...)  (!log_is_enabled(Info, __VA_ARGS__))   ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Info>
+#define log_develop_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Debug>
+#define log_develop_trace(...) (!log_is_enabled(Trace, __VA_ARGS__))  ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Trace>
+#define develop_log_is_enabled(level, ...)  log_is_enabled(level, __VA_ARGS__)
 #else
 #define DUMMY_ARGUMENT_CONSUMER(...)
-#define log_develop(...) DUMMY_ARGUMENT_CONSUMER
+#define log_develop_info(...)  DUMMY_ARGUMENT_CONSUMER
+#define log_develop_debug(...) DUMMY_ARGUMENT_CONSUMER
+#define log_develop_trace(...) DUMMY_ARGUMENT_CONSUMER
+#define develop_log_is_enabled(...)  false
 #endif
 
 // Convenience macro to test if the logging is enabled on the specified level for given tags.
@@ -88,6 +98,11 @@
   // is not __NO_TAG, the number of tags given exceeds the maximum allowed.
   STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported!
 
+  // Empty constructor to avoid warnings on MSVC about unused variables
+  // when the log instance is only used for static functions.
+  Log() {
+  }
+
   static bool is_level(LogLevelType level) {
     return LogTagSetMapping<T0, T1, T2, T3, T4>::tagset().is_level(level);
   }
--- a/hotspot/src/share/vm/logging/logConfiguration.cpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/logging/logConfiguration.cpp	Mon Nov 23 21:00:25 2015 +0000
@@ -44,6 +44,7 @@
   LogDiagnosticCommand::registerCommand();
   LogHandle(logging) log;
   log.info("Log configuration fully initialized.");
+  log_develop_info(logging)("Develop logging is available.");
   if (log.is_trace()) {
     ResourceMark rm;
     MutexLocker ml(LogConfiguration_lock);
--- a/hotspot/src/share/vm/logging/logLevel.hpp	Fri Nov 20 11:57:33 2015 -0600
+++ b/hotspot/src/share/vm/logging/logLevel.hpp	Mon Nov 23 21:00:25 2015 +0000
@@ -29,14 +29,8 @@
 
 // The list of log levels:
 //
-//  develop - A non-product level that is finer than trace.
-//            Should be used for really expensive and/or
-//            extensive logging, or logging that shouldn't
-//            or can't be included in a product build.
-//
-//  trace   - Finest level of logging in product builds.
-//            Use for extensive/noisy logging that can
-//            give slow-down when enabled.
+//  trace   - Finest level of logging. Use for extensive/noisy
+//            logging that can give slow-down when enabled.
 //
 //  debug   - A finer level of logging. Use for semi-noisy
 //            logging that is does not fit the info level.
@@ -49,7 +43,6 @@
 //  error   - Critical messages caused by errors.
 //
 #define LOG_LEVEL_LIST \
-  NOT_PRODUCT(LOG_LEVEL(Develop, develop)) \
   LOG_LEVEL(Trace, trace) \
   LOG_LEVEL(Debug, debug) \
   LOG_LEVEL(Info, info) \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ThreadSignalMask/Prog.java	Mon Nov 23 21:00:25 2015 +0000
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class Prog {
+
+    public static void main(String args[]) {
+        System.out.println("Java class invoked: " + args[0]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ThreadSignalMask/ThreadSignalMask.java	Mon Nov 23 21:00:25 2015 +0000
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.ProcessBuilder.Redirect;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.List;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @key cte_test
+ * @bug 4345157
+ * @summary JDK 1.3.0 alters thread signal mask
+ * @requires (os.simpleArch == "sparcv9")
+ * @library /testlibrary
+ * @compile Prog.java
+ * @run main/native ThreadSignalMask
+ */
+public class ThreadSignalMask {
+
+    public static void main(String args[]) throws Exception {
+
+        String testClasses = getSystemProperty("test.classes");
+
+        String testNativePath = getSystemProperty("test.nativepath");
+
+        String testJdk = getSystemProperty("test.jdk");
+
+        Path currentDirPath = Paths.get(".");
+
+        Path classFilePath = Paths.get(testClasses,
+                Prog.class.getSimpleName() + ".class");
+
+        // copy Prog.class file to be invoked from native
+        Files.copy(classFilePath,
+                currentDirPath.resolve(Prog.class.getSimpleName() + ".class"),
+                StandardCopyOption.REPLACE_EXISTING);
+
+        Path executableFilePath = Paths.get(testNativePath,
+                ThreadSignalMask.class.getSimpleName());
+
+        Path executableFileLocalPath = currentDirPath.resolve(
+                ThreadSignalMask.class.getSimpleName());
+
+        // copy compiled native executable ThreadSignalMask
+        Files.copy(executableFilePath,
+                executableFileLocalPath,
+                StandardCopyOption.REPLACE_EXISTING);
+
+        executableFileLocalPath.toFile().setExecutable(true);
+
+        long[] intervalsArray = {2000, 5000, 10000, 20000};
+
+        List<String> processArgs = Arrays.asList(
+                executableFileLocalPath.toString(),
+                testJdk);
+        ProcessBuilder pb = new ProcessBuilder(processArgs);
+        pb.redirectOutput(Redirect.INHERIT);
+        pb.redirectError(Redirect.INHERIT);
+        int result = 0;
+        for (long interval : intervalsArray) {
+            Process p = pb.start();
+
+            // sleep for a specified period of time to let native run
+            sleep(interval);
+            p.destroy();
+
+            // wait for process to finish, get exit value and validate it
+            result = p.waitFor();
+            System.out.println("Result = " + result);
+            if (result == 0) {
+                break;
+            }
+        }
+
+        Asserts.assertEquals(result, 0);
+    }
+
+    // Utility method to handle Thread.sleep
+    private static void sleep(long millis) throws InterruptedException {
+        System.out.println("Sleep for " + millis);
+        Thread.sleep(millis);
+    }
+
+    // Utility method to retrieve and validate system properties
+    private static String getSystemProperty(String propertyName) throws Error {
+        String systemProperty = System.getProperty(propertyName, "").trim();
+        System.out.println(propertyName + " = " + systemProperty);
+        if (systemProperty.isEmpty()) {
+            throw new Error("TESTBUG: property " + propertyName + " is empty");
+        }
+        return systemProperty;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ThreadSignalMask/exeThreadSignalMask.c	Mon Nov 23 21:00:25 2015 +0000
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define _POSIX_PTHREAD_SEMANTICS // to enable POSIX semantics for certain common APIs
+
+#include <jni.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+void *handle;
+char *error;
+char path[PATH_MAX];
+
+jint(JNICALL *jni_create_java_vm)(JavaVM **, JNIEnv **, void *) = NULL;
+
+JavaVM *jvm;
+
+// method to perform dlclose on an open dynamic library handle
+void closeHandle() {
+  dlclose(handle);
+  if ((error = dlerror()) != NULL) {
+    fputs("Error occurred while closing handle\n", stderr);
+  }
+}
+
+// method to exit with a fail status
+void fail() {
+  if (handle) {
+    closeHandle();
+  }
+  exit(1);
+}
+
+// method to handle occurred error and fail
+void handleError(char *messageTitle, char *messageBody) {
+  fprintf(stderr, "%s: %s\n", messageTitle, messageBody);
+  fail();
+}
+
+// method to load the dynamic library libjvm
+void loadJVM() {
+  char lib[PATH_MAX];
+  snprintf(lib, sizeof (lib), "%s/lib/sparcv9/server/libjvm.so", path);
+  handle = dlopen(lib, RTLD_LAZY);
+  if (!handle) {
+    handleError(dlerror(), "2");
+  }
+  fputs("Will load JVM...\n", stdout);
+
+  // find the address of function
+  *(void **) (&jni_create_java_vm) = dlsym(handle, "JNI_CreateJavaVM");
+  if ((error = dlerror()) != NULL) {
+    handleError(error, "3");
+  }
+
+  fputs("JVM loaded okay.\n", stdout);
+}
+
+// method to get created jvm environment
+JNIEnv* initJVM() {
+  JNIEnv *env = NULL;
+  JavaVMInitArgs vm_args;
+  JavaVMOption options[1];
+  jint res;
+
+  options[0].optionString = "-Xrs";
+
+  vm_args.version = JNI_VERSION_1_2;
+  vm_args.nOptions = 1;
+  vm_args.options = options;
+  vm_args.ignoreUnrecognized = JNI_FALSE;
+
+  fputs("Will create JVM...\n", stdout);
+
+  res = (*jni_create_java_vm)(&jvm, &env, &vm_args);
+  if (res < 0) {
+    handleError("Can't create Java VM", strerror(res));
+  }
+
+  fputs("JVM created OK!\n", stdout);
+  return env;
+}
+
+// method to invoke java method from java class
+void callJava(JNIEnv *env) {
+  jclass cls;
+  jmethodID mid;
+  jstring jstr;
+  jobjectArray args;
+
+  cls = (*env)->FindClass(env, "Prog");
+  if (cls == 0) {
+    handleError("FindClass", "Can't find Prog class");
+  }
+
+  mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
+  if (mid == 0) {
+    handleError("GetStaticMethodID", "Can't find Prog.main");
+  }
+
+  jstr = (*env)->NewStringUTF(env, "from C!");
+  if (jstr == 0) {
+    handleError("NewStringUTF", "Out of memory");
+  }
+  args = (*env)->NewObjectArray(env, 1,
+          (*env)->FindClass(env, "java/lang/String"), jstr);
+  if (args == 0) {
+    handleError("NewObjectArray", "Out of memory");
+  }
+  (*env)->CallStaticVoidMethod(env, cls, mid, args);
+
+}
+
+// method to load, init jvm and then invoke java method
+void* loadAndCallJava(void* x) {
+  JNIEnv *env;
+
+  fputs("Some thread will create JVM.\n", stdout);
+  loadJVM();
+  env = initJVM();
+
+  fputs("Some thread will call Java.\n", stdout);
+
+  callJava(env);
+
+  if ((*jvm)->DetachCurrentThread(jvm) != 0)
+    fputs("Error: thread not detached!\n", stderr);
+  fputs("Some thread exiting.\n", stdout);
+  return env;
+}
+
+int main(int argc, char **argv) {
+  JNIEnv *env;
+  sigset_t set;
+  pthread_t thr1;
+  pthread_attr_t attr;
+  size_t ss = 0;
+  int sig;
+  int rc; // return code for pthread_* methods
+
+  // verify input
+  if (argc != 2) {
+    handleError("usage", "a.out jdk_path");
+  }
+  // copy input jdk path into a char buffer
+  strncpy(path, argv[1], PATH_MAX);
+  // add null termination character
+  path[PATH_MAX - 1] = '\0';
+
+  fputs("Main thread will set signal mask.\n", stdout);
+
+  // initialize the signal set
+  sigemptyset(&set);
+  // add a number of signals to a signal set
+  sigaddset(&set, SIGPIPE);
+  sigaddset(&set, SIGTERM);
+  sigaddset(&set, SIGHUP);
+  sigaddset(&set, SIGINT);
+
+  // examine and change mask of blocked signal
+  if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL))) {
+    // handle error if occurred
+    handleError("main: pthread_sigmask() error", strerror(rc));
+  }
+
+  // initializes the thread attributes object with default attribute values
+  if ((rc = pthread_attr_init(&attr))) {
+    // handle error if occurred
+    handleError("main: pthread_attr_init() error", strerror(rc));
+  }
+
+  ss = 1024 * 1024;
+  // set the stack size attribute of the thread attributes object
+  if ((rc = pthread_attr_setstacksize(&attr, ss))) {
+    // handle error if occurred
+    handleError("main: pthread_attr_setstacksize() error", strerror(rc));
+  }
+  // get the stack size attribute of the thread attributes object
+  if ((rc = pthread_attr_getstacksize(&attr, &ss))) {
+    // handle error if occurred
+    handleError("main: pthread_attr_getstacksize() error", strerror(rc));
+  }
+  fprintf(stderr, "Stack size: %zu\n", ss);
+
+  // start a new thread in the calling process,
+  // loadAndCallJava logic is passed as a start_routine argument
+  if ((rc = pthread_create(&thr1, NULL, loadAndCallJava, NULL))) {
+    // handle error if occurred
+    handleError("main: pthread_create() error", strerror(rc));
+  }
+
+  // initialize the signal set
+  sigemptyset(&set);
+  // add a number of signals to a signal set
+  sigaddset(&set, SIGTERM);
+  sigaddset(&set, SIGHUP);
+  sigaddset(&set, SIGINT);
+
+  fputs("Main thread waiting for signal.\n", stdout);
+
+  do {
+    int err;
+
+    sig = 0;
+    err = sigwait(&set, &sig);
+    if (err != 0) {
+      // print error message if unexpected signal occurred
+      fprintf(stderr, "main: sigwait() error:  %s\n", strerror(err));
+    } else {
+      // print success message and exit if expected signal occurred
+      // this branch generally acts when JVM executes destroy()
+      fprintf(stdout, "main: sigwait() got:  %d\nSucceed!\n", sig);
+      exit(0);
+    }
+  } while (sig != SIGTERM && sig != SIGINT); // exit the loop condition
+
+  // join with a terminated thread
+  if ((rc = pthread_join(thr1, NULL))) {
+    // handle error if occurred
+    handleError("main: pthread_join() error", strerror(rc));
+  }
+
+  // close an open dynamic library handle
+  closeHandle();
+  fputs("Main thread exiting.\n", stdout);
+  return 0;
+}