/*
 * Copyright 2005 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/*
 * @test
 * @bug     4519200
 * @summary Confirm a Thread.stop before start complies with the spec
 * @author  Pete Soper
 *
 * Confirm that a thread that had its stop method invoked before start
 * does properly terminate with expected exception behavior. NOTE that
 * arbitrary application threads could return from their run methods faster
 * than the VM can throw an async exception.
 */
public class StopBeforeStart {

    private static final int JOIN_TIMEOUT=10000;

    private class MyThrowable extends Throwable {
    }

    private class Catcher implements Thread.UncaughtExceptionHandler {
        private boolean nullaryStop;
        private Throwable theThrowable;
        private Throwable expectedThrowable;
        private boolean exceptionThrown;

        Catcher(boolean nullaryStop) {
            this.nullaryStop = nullaryStop;
            if (!nullaryStop) {
                expectedThrowable = new MyThrowable();
            }
        }

        public void uncaughtException(Thread t, Throwable th) {
            exceptionThrown = true;
            theThrowable = th;
        }

        void check(String label) throws Throwable {
            if (!exceptionThrown) {
                throw new RuntimeException(label +
                        " test:" + " missing uncaught exception");
            }

            if (nullaryStop) {
                if (! (theThrowable instanceof ThreadDeath)) {
                    throw new RuntimeException(label +
                        " test:" + " expected ThreadDeath in uncaught handler");
                }
            } else if (theThrowable != expectedThrowable) {
                throw new RuntimeException(label +
                        " test:" + " wrong Throwable in uncaught handler");
            }
        }
    }

    private class MyRunnable implements Runnable {
        public void run() {
            while(true)
                ;
        }
    }

    private class MyThread extends Thread {
        public void run() {
            while(true)
                ;
        }
    }


    public static void main(String args[]) throws Throwable {
        (new StopBeforeStart()).doit();
        System.out.println("Test passed");
    }

    private void doit() throws Throwable {

        runit(false, new Thread(new MyRunnable()),"Thread");
        runit(true, new Thread(new MyRunnable()),"Thread");
        runit(false, new MyThread(),"Runnable");
        runit(true, new MyThread(),"Runnable");
    }

    private void runit(boolean nullaryStop, Thread thread,
                        String type) throws Throwable {

        Catcher c = new Catcher(nullaryStop);
        thread.setUncaughtExceptionHandler(c);

        if (nullaryStop) {
            thread.stop();
        } else {
            thread.stop(c.expectedThrowable);
        }

        thread.start();
        thread.join(JOIN_TIMEOUT);

        if (thread.getState() != Thread.State.TERMINATED) {

            thread.stop();

            // Under high load this could be a false positive
            throw new RuntimeException(type +
                        " test:" + " app thread did not terminate");
        }

        c.check(type);
    }
}