FULL PRODUCT VERSION :
java version "1.6.0_10-rc2"
Java(TM) SE Runtime Environment (build 1.6.0_10-rc2-b32)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
I reported this a few days ago, but I didn't realize how serious this was, and I have a much better example for reproducing it now.
Setting the JVM's SecurityManager may cause the JVM to quit before all non-daemon threads have quit. I'm unsure of exactly what the parameters are that cause this, but for a given SecurityManager, it will seemingly always happen, or never happen.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run test case below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Should print out some data from the SecurityManager, then slowly print the numbers 1-1000.
ACTUAL -
Prints out data from the SecurityManager, then prints "0" (sometimes), and quits.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package org.thedreaming.test;
import java.io.FileDescriptor;
import java.net.InetAddress;
import java.security.Permission;
public class SecurityManagerTest {
public static void main(String[] args) {
final SecurityManager securityManager = new SecurityManager() {
@Override
public void checkMemberAccess(Class<?> clazz, int which) {
System.out.println("" + Thread.currentThread() + ": Checking access for " + clazz + " for " + which);
}
@Override
public void checkPackageAccess(String pkg){
System.out.println("" + Thread.currentThread() + ": Checking access for package " + pkg);
}
@Override
public void checkAccept(String host, int port) {
System.out.println("" + Thread.currentThread() + ": Accept");
}
@Override
public void checkAccess(Thread t) {
System.out.println("" + Thread.currentThread() + ": Access " + t);
}
@Override
public void checkAccess(ThreadGroup g) {
System.out.println("" + Thread.currentThread() + ": Access " + g);
}
@Override
public void checkAwtEventQueueAccess() {
System.out.println("" + Thread.currentThread() + ": AWT");
}
@Override
public void checkConnect(String host, int port, Object context) {
System.out.println("" + Thread.currentThread() + ": Connect for context");
}
@Override
public void checkConnect(String host, int port) {
System.out.println("" + Thread.currentThread() + ": Connect");
}
@Override
public void checkCreateClassLoader() {
System.out.println("" + Thread.currentThread() + ": Create class loader");
}
@Override
public void checkDelete(String file) {
System.out.println("" + Thread.currentThread() + ": Checking delete");
}
@Override
public void checkExec(String cmd) {
System.out.println("" + Thread.currentThread() + ": Checking exec");
}
@Override
public void checkExit(int status) {
super.checkExit(status);
}
@Override
public void checkLink(String lib) {
System.out.println("" + Thread.currentThread() + ": Checking link");
}
@Override
public void checkListen(int port) {
System.out.println("" + Thread.currentThread() + ": Checking listen");
}
@Override
public void checkMulticast(InetAddress maddr) {
System.out.println("" + Thread.currentThread() + ": Checking multicast");
}
@Override
public void checkPackageDefinition(String pkg) {
System.out.println("" + Thread.currentThread() + ": Checking pkg def");
}
@Override
public void checkPermission(Permission perm, Object context) {
System.out.println("" + Thread.currentThread() + ": Checking permission " + perm + " for context");
}
@Override
public void checkPermission(Permission perm) {
System.out.println("" + Thread.currentThread() + ": Checking permission " + perm);
}
@Override
public void checkPrintJobAccess() {
System.out.println("" + Thread.currentThread() + ": Checking print job");
}
@Override
public void checkPropertiesAccess() {
System.out.println("" + Thread.currentThread() + ": Checking property access");
}
@Override
public void checkPropertyAccess(String key) {
System.out.println("" + Thread.currentThread() + ": Checking property access: " + key);
}
@Override
public void checkRead(FileDescriptor fd) {
System.out.println("" + Thread.currentThread() + ": Checking read fd " + fd);
}
@Override
public void checkRead(String file, Object context) {
System.out.println("" + Thread.currentThread() + ": Checking read for context");
}
@Override
public void checkRead(String file) {
System.out.println("" + Thread.currentThread() + ": Checking read " + file);
}
@Override
public void checkSecurityAccess(String target) {
System.out.println("" + Thread.currentThread() + ": Checking security access");
}
@Override
public void checkSetFactory() {
System.out.println("" + Thread.currentThread() + ": Checking set factory");
}
@Override
public void checkSystemClipboardAccess() {
System.out.println("" + Thread.currentThread() + ": Checking clipboard access");
}
@Override
public boolean checkTopLevelWindow(Object window) {
System.out.println("" + Thread.currentThread() + ": Checking top level window");
return super.checkTopLevelWindow(window);
}
@Override
public void checkWrite(FileDescriptor fd) {
System.out.println("" + Thread.currentThread() + ": Checking write fd " + fd);
}
@Override
public void checkWrite(String file) {
System.out.println("" + Thread.currentThread() + ": Checking write " + file);
}
};
Thread t = new Thread() {
public void run() {
System.setSecurityManager(securityManager);
}
};
t.start();
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
}
Runnable runnable = new Runnable() {
public void run() {
for(int i = 0; i < 1000; i++) {
System.out.println("" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
System.out.println("Hello World");
System.out.flush();
}
};
Thread thread = new Thread(runnable);
thread.start();
System.out.println("Started " + thread.getName() + " " + thread.isDaemon() + " " + thread.isAlive());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
If you can keep track of all the threads you spawn, then "thread.join()" in the main thread for each child thread seems to fix this, but this is not an option in my case.