JDK-4168811 : (thread) ThreadGroup is not empty after last Thread dies
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.2.0
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: windows_nt
  • CPU: x86
  • Submitted: 1998-08-25
  • Updated: 2005-09-19
  • Resolved: 2005-09-19
Related Reports
Relates :  
Relates :  
Description
Name: akC45999			Date: 08/25/98



The description of java.lang.ThreadGroup.destroy() reads:

Throws:
  IllegalThreadStateException - if the thread group is not empty or if the thread group has
  already been destroyed.


But in JDK1.2 FCS-G/windowsNT, the method throws IllegalThreadStateException when group
is empty and not yet destroyed.

-------------------------------------------------- file destroy10101.java
//File: @(#)destroy0101.java 1.2 98/07/02 
//Copyright 07/02/98 Sun Microsystems, Inc.  All Rights Reserved

//package javasoft.sqe.tests.api.java.lang.ThreadGroup.destroy0101;

import java.io.PrintStream;
import javasoft.sqe.harness.Status;
import javasoft.sqe.harness.Test;

class destroy0101t extends Thread {
  destroy0101 event;

  destroy0101t(destroy0101g group, destroy0101 event) {
	super(group, group.groupName+".activeThrd");
	this.event=event;
  }

  public void run() {
	event.run();
  }

} // end class destroy0101t


class destroy0101g extends ThreadGroup {

  static final int activeNum=1; // active threads in each group
  String groupName;
  Thread activeThrd;
  destroy0101g subgroup;
  destroy0101 event;


  void finish() throws InterruptedException {
	if (subgroup!=null) subgroup.finish();
	activeThrd.join();
  }

  destroy0101g(ThreadGroup parent, destroy0101 event, int level) {
	super("destroy0101g_"+level);
	this.event=event;
	groupName="destroy0101g_"+level;
	activeThrd=new destroy0101t(this, event);
	activeThrd.start();
	if (level>0)
		subgroup=new destroy0101g(this, event, level-1);
  }

} // end class destroy0101g



public class destroy0101  implements Test {

  boolean end=false;

  public synchronized void run() {
	while (!end) {;
		try {
			wait();
		} catch (InterruptedException e) {
		}
	}
  }

  public synchronized void stopAll() {
	end=true;
	notifyAll();
  }

  public Status run(String argv[], PrintStream log, PrintStream out) {
	destroy0101g group=new destroy0101g(Thread.currentThread().getThreadGroup(), this, 5);

	try {
		group.destroy();
		return Status.failed("no IllegalThreadStateException 1");
	} catch (IllegalThreadStateException e) {
	} finally {
		stopAll();
	}

	try {
		group.finish();
	} catch (InterruptedException e) {
		return Status.failed("InterruptedException while joining child thread.");
	}

	try {
		group.destroy();
	} catch (Throwable e) {
		return Status.failed("unexpected exception:"+e);
	}

	if (!group.isDestroyed()) {
		return Status.failed("destroy() called but group !isDestroyed()");
	}

	try {
		group.destroy();
		return Status.failed("no IllegalThreadStateException 2");
	} catch (IllegalThreadStateException e) {
	}

    return Status.passed(""); 
  }

  public static void main(String args[]) {
	(new destroy0101()).run(args, System.err, System.out).exit();
  }

}

----------------------------
output:

unexpected exception: IllegalThreadStateException


======================================================================

Name: akC45999			Date: 10/06/98


A Fujitsu engeneer suggested to add a call to Thread.sleep()
between group.finish() and group.destroy() to help the test to pass.
This does work both on Windows 95 and WindowsNT.

Another test (activeCount0102) just counts active threads but behaves identically
(sleep(delay) makes the test to pass).

So I assume that a Thread Group does not become empty immediately
after the last thread dies, but some time later.

//------------------- activeCount0102.java
// simplified version of the test javasoft.sqe.tests.api.java.lang.ThreadGroup.activeCount0102
import java.io.PrintStream;

public class activeCount0102 {

  public static void main(String argv[]) {
	long delay=((argv.length==0)?-1:Long.parseLong(argv[0]));
	ThreadGroup group=new ThreadGroup("activeCount0102_g");
	Thread nonActiveThrd1;
	nonActiveThrd1=new Thread(group, "activeCount0102_n");
	nonActiveThrd1.start();
	try {
		nonActiveThrd1.join();
		if (delay>=0) Thread.sleep(delay);
	} catch (InterruptedException e) {
		System.err.println("InterruptedException in main thread");
	}

	int cnt0=group.activeCount();
	if (cnt0 != 0)
		System.err.println("Failed: activeCount()="+cnt0);
	else
		System.err.println("Passed.");
  }

}

//------------------- end activeCount0102.java
run it with command
java activeCount0102 <delay>
Even with delay=0, test sometimes pass.


======================================================================

Comments
EVALUATION This defect was corrected prior to version 1.2 but the actual CR it's a dupe of could not be determined.
19-09-2005

EVALUATION I think the entire spec for ThreadGroup is relative weak, many methods can not be implemented in strict semantics. It should be better if we can have a tight spec, and reconsider the stuff as a whole thing. ###@###.### 1998-11-03 I just spoke with Hong and he states this is too risky fix in 1.1.8. debra.chapatte@eng 1998-11-04
03-11-1998