/*
 * Copyright (c) 2002, 2018, 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.
 */


/*
 * @test
 * @key gc
 *
 * @summary converted from VM Testbase gc/gctests/WeakReferenceGC.
 * VM Testbase keywords: [gc, nonconcurrent]
 * VM Testbase readme:
 * *******************************************
 * set timeout = 25 when running this test
 * *******************************************
 * This tests checks to see if the garbage collector enqueues
 * a weak reference when referrent has been turned to garbage.
 * Weak references are enqueued in  a non-deterministic way
 * by the garbage collector, so the test uses a heuristic to
 * determine whether the references are being enqueued in a timely
 * manner which in turn determines whether outcome of the test.
 * IF the user invokes the test with the following command line
 * parameters :
 *   java WeakReferenceGC -qFactor 0.9 -gcCount 5
 * the test expects that 90% of all objects with only weak references
 * pointing to them will be enqueued within 5 calls to the garbage collector.
 * When I ran the test, I consistently got figures of 98% enqueueing
 * with just 1 call to the garbage collector. So if this test fails,
 * at its current settings, the garbage collector is not performing as well
 * as it used to.
 * The test creates circular linked lists of size 0.1Meg each. The number
 * of lists created can be set via the -numLists flag. The default
 * value is 50.
 * The circular linked lists have both strong and weak references pointing
 * to them. The strong and weak references are kept in arrays.
 * The strong references are all nulled out and System.gc() is called
 * explicitly and the heuristic is applied. If the test does not
 * satisfy the heuristic or an OutOfMemory exception is thrown,
 * the test fails.
 * Array containing    Each circular linked list        Array containing
 * weak references     is 0.1 Meg each and has          strong references
 * to linked lists.    a weak reference, W<n> and a     to linked lists.
 *                     strong reference, x<n>
 *                     pointing to it.
 *  ----      ----------------------------        -----
 * |    |     |   ----             ---- <-|      |     |
 * | W1 |-->  -->|    |---.......>|    |   <---- |  x1 |
 * |    |     -->|    |<---.......|    |<--      |     |
 *  ----      |   ----       1000  ---   |        -----
 *            ---------------------------
 *   .                                              .
 *   .                                              .
 *   .                                              .
 *   .                                              .
 *   .                                              .  10
 *   .                                              .
 *  ----      ----------------------------        -----
 * |    |     |   ----             ---- <-|      |     |
 * | Wn |-->  -->|    |---.......>|    |   <---- |  xn |
 * |    |     -->|    |<---.......|    |<--      |     |
 *  ----      |   ----       1000  ---   |        -----
 *            ---------------------------
 *
 * @library /vmTestbase
 *          /test/lib
 * @run driver jdk.test.lib.FileInstaller . .
 * @run main/othervm
 *      gc.gctests.WeakReferenceGC.WeakReferenceGC
 *      -numList 50
 *      -qFactor 0.9
 *      -gcCount 5
 *      -iter 100
 */

package gc.gctests.WeakReferenceGC;

import java.util.*;
import java.lang.ref.*;
import nsk.share.TestFailure;
import nsk.share.gc.GC;
import nsk.share.gc.ThreadedGCTest;
import nsk.share.gc.gp.GarbageUtils;

public class WeakReferenceGC extends ThreadedGCTest {

        // A heuristic is used to determine the outcome(pass/fail
        // status) of a test. If 90% of all objects that have
        // _only_ weak references pointing to them are garbage
        // collected with 5 explicit calls to the garbage collector
        // the test is deemed a pass. The following two variables
        // are used to define this heuristic: gcCount, qFactor

        static String args[];

        CircularLinkedList holder[];

        int loopCount = 100; // # of times test is performed

        int memory_reserve[];
        int gcCount = 5;
        int numLists = 50; // number of linked lists
        float qFactor = (float) 0.9;
        ReferenceQueue refQueue;
        Vector results;
        WeakReference wholder[];

        public static void main(String[] args) {
                WeakReferenceGC.args = args;
                GC.runTest(new WeakReferenceGC(), args);
        }

        WeakReferenceGC() {
                holder = new CircularLinkedList[numLists];
                wholder = new WeakReference[numLists];
                refQueue = new ReferenceQueue();
                results = new Vector();
        }

        protected Runnable createRunnable(int i) {
                return i > 0 ? null : new Worker();
        }

        private void dumpTestResults() {
                double objectsRecovered;

                System.out.println("Percentage of Objects" + "  # of GC's");
                System.out.println("    Recovered         " + " Required");
                for (int i = 0; i < results.size(); i++) {
                        Statistic s = (Statistic) results.elementAt(i);
                        objectsRecovered = Math.rint((float) (s.numEnqueued)
                                        / (float) (numLists) * 100.0);
                        System.out.println("        " + objectsRecovered
                                        + " %             " + s.iterations);
                }
        }

        private boolean hasPassed() {
                boolean passed;
                passed = true; // assume passed till proven otherwise

                for (int i = 0; i < results.size(); i++) {
                        Statistic s = (Statistic) results.elementAt(i);
                        if ((s.iterations > gcCount)
                                        || (s.numEnqueued < (int) (numLists * qFactor))) {
                                passed = false;
                                break; // test failed
                        }
                }
                return passed;
        }

        private void parseTestParams(String args[]) {
                for (int i = 0; i < args.length; i++) {
                        if (args[i].compareTo("-numList") == 0) {
                                numLists = new Integer(args[++i]).intValue();
                        } else if (args[i].compareTo("-qFactor") == 0) {
                                qFactor = new Float(args[++i]).floatValue();
                        } else if (args[i].compareTo("-gcCount") == 0) {
                                gcCount = new Integer(args[++i]).intValue();
                        } else if (args[i].compareTo("-iter") == 0) {
                                loopCount = new Integer(args[++i]).intValue();
                                // } else {
                                // System.err.println("usage : " +
                                // "java WeakReferenceGC [-numList <n>] " +
                                // "[-qFactor <0.x>] [-gcCount <n>] [-iter <n>]");
                                // throw new TestBug("Invalid arguments");
                        }
                }
        }

        private void persistentGC() {
                int numEnqueued, iter, qCriterion;

                numEnqueued = 0; // number of weakReference enqueued
                iter = 0;
                qCriterion = (int) (numLists * qFactor);

                while ((numEnqueued < qCriterion) && (iter <= gcCount)) {
                        iter++;
                        if (!getExecutionController().continueExecution()) {
                                return;
                        }
                        if (GarbageUtils.eatMemory(getExecutionController()) == 0) {
                                return; // We were unable to provoke OOME before timeout is over
                        }
                        numEnqueued = 0; // We set counter to zero to avoid counting references twice
                        for (int i = 0; i < numLists; i++) {
                                if (wholder[i].isEnqueued()) {
                                        numEnqueued++;
                                }
                        }
                }
                results.addElement((new Statistic(iter, numEnqueued)));
        }

        private void runTest() {
                int iter;
                iter = 1;
                try {
                        do {
                                for (int i = 0; i < numLists; i++) {
                                        holder[i] = new CircularLinkedList();
                                        holder[i].addNelements(1000);
                                        wholder[i] = new WeakReference(holder[i], refQueue);
                                }

                                for (int i = 0; i < numLists; i++) {
                                        holder[i] = null;
                                }

                                if (!getExecutionController().continueExecution()) {
                                        return;
                                }
                                persistentGC();

                                iter++;
                        } while (iter <= loopCount);
                } catch (OutOfMemoryError e) {
                        memory_reserve = null;
                        System.gc();
                        throw new TestFailure("Failed at iteration=" + loopCount);
                }
        }

        //We can't override run() method in WeakReferenceGC, so we are carrying out it to Worker
        private class Worker implements Runnable {
                @Override
                public void run() {
                        parseTestParams(args);
                        runTest();
                        dumpTestResults();
                        boolean passed = hasPassed();
                        if (passed == true) {
                                log.info("Test passed.");
                        } else {
                                log.error("Test failed.");
                                setFailed(true);
                        }
                }
        }
}

class Statistic {
        int iterations;
        int numEnqueued;

        Statistic(int i, int num) {
                iterations = i;
                numEnqueued = num;
        }

}
