tmf: Add an attribute pool for state system analyses
authorGeneviève Bastien <gbastien+lttng@versatic.net>
Wed, 24 Feb 2016 16:24:41 +0000 (11:24 -0500)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Tue, 5 Apr 2016 00:20:41 +0000 (20:20 -0400)
This new class allows to reuse attribute quarks in a state system. It can
typically be used by analyses who want to save data in the state system
for short intervals of time. Instead of creating a number of attributes with
lots of empty space before and after, they can reuse the same. It makes for
more compact time graph views as well.

Change-Id: Ie7f32eccb02096ef62df4c479a8e2bcfcd043af6
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/67263
Reviewed-by: Hudson CI
Reviewed-by: Alexandre Montplaisir <alexmonthy@efficios.com>
tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/statesystem/AttributePoolTest.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/TmfAttributePool.java [new file with mode: 0644]

diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/statesystem/AttributePoolTest.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/statesystem/AttributePoolTest.java
new file mode 100644 (file)
index 0000000..39bda59
--- /dev/null
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.core.tests.statesystem;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.StateSystemFactory;
+import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
+import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
+import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfAttributePool;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfAttributePool.QueueType;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Test the {@link TmfAttributePool} class
+ *
+ * @author Geneviève Bastien
+ */
+@NonNullByDefault
+public class AttributePoolTest {
+
+    private static final long START_TIME = 1000L;
+    private static final String DUMMY_STRING = "test";
+    private static final ITmfStateValue VALUE = TmfStateValue.newValueInt(2);
+
+    private ITmfStateSystemBuilder fStateSystem;
+
+    /**
+     * Initialize the state system
+     */
+    public AttributePoolTest() {
+        IStateHistoryBackend backend = StateHistoryBackendFactory.createInMemoryBackend(DUMMY_STRING, START_TIME);
+        fStateSystem = StateSystemFactory.newStateSystem(backend);
+    }
+
+    /**
+     * Clean-up
+     */
+    @After
+    public void tearDown() {
+        fStateSystem.dispose();
+    }
+
+    /**
+     * Test the constructor of the attribute pool
+     */
+    @Test
+    public void testConstructorGood() {
+        // Create an attribute pool with ROOT_QUARK as base
+        TmfAttributePool pool = new TmfAttributePool(fStateSystem, ITmfStateSystem.ROOT_ATTRIBUTE);
+        assertNotNull(pool);
+
+        int quark = fStateSystem.getQuarkAbsoluteAndAdd(DUMMY_STRING);
+        pool = new TmfAttributePool(fStateSystem, quark);
+    }
+
+    /**
+     * Test the constructor with an invalid attribute
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testConstructorBad() {
+        new TmfAttributePool(fStateSystem, ITmfStateSystem.INVALID_ATTRIBUTE);
+    }
+
+    /**
+     * Test the constructor with an invalid positive attribute
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testConstructorBad2() {
+        new TmfAttributePool(fStateSystem, 3);
+    }
+
+    /**
+     * Test attributes using only one level of children
+     */
+    @Test
+    public void testSimplePool() {
+        TmfAttributePool pool = new TmfAttributePool(fStateSystem, ITmfStateSystem.ROOT_ATTRIBUTE);
+        assertNotNull(pool);
+
+        /* Get some attributes */
+        Integer available = pool.getAvailable();
+        assertNotNull(available);
+        Integer available2 = pool.getAvailable();
+        assertNotNull(available2);
+        assertNotEquals(available, available2);
+
+        /* Verify the names of the attributes */
+        assertEquals("0", fStateSystem.getAttributeName(available));
+        assertEquals("1", fStateSystem.getAttributeName(available2));
+
+        /* Modify them */
+        try {
+            fStateSystem.modifyAttribute(START_TIME + 10, VALUE, available);
+            fStateSystem.modifyAttribute(START_TIME + 10, VALUE, available2);
+        } catch (StateValueTypeException | AttributeNotFoundException e) {
+            fail(e.getMessage());
+        }
+
+        /* Recycle one and make sure it is set to null */
+        pool.recycle(available, START_TIME + 20);
+        try {
+            ITmfStateValue value = fStateSystem.queryOngoingState(available);
+            assertEquals(TmfStateValue.nullValue(), value);
+        } catch (AttributeNotFoundException e) {
+            fail(e.getMessage());
+        }
+
+        /* Get a new one and make sure it is reusing the one just recycled */
+        Integer available3 = pool.getAvailable();
+        assertEquals(available, available3);
+
+        /* Get a new attribute and make sure it is different from both other used attributes */
+        Integer available4 = pool.getAvailable();
+        assertNotEquals(available3, available4);
+        assertNotEquals(available2, available4);
+
+        /* Recycle available attributes, in reverse order and see how they are returned */
+        pool.recycle(available4, START_TIME + 30);
+        pool.recycle(available2, START_TIME + 30);
+        pool.recycle(available3, START_TIME + 30);
+
+        Integer available5 = pool.getAvailable();
+        assertEquals(available4, available5);
+
+        Integer available6 = pool.getAvailable();
+        assertEquals(available2, available6);
+
+        Integer available7 = pool.getAvailable();
+        assertEquals(available3, available7);
+    }
+
+    /**
+     * Test attributes with sub-trees
+     */
+    @Test
+    public void testPoolWithSubTree() {
+        TmfAttributePool pool = new TmfAttributePool(fStateSystem, ITmfStateSystem.ROOT_ATTRIBUTE);
+        assertNotNull(pool);
+
+        /* Get some attributes */
+        Integer available = pool.getAvailable();
+        assertNotNull(available);
+
+        /* Add children and set values for them */
+        try {
+            Integer child1 = fStateSystem.getQuarkRelativeAndAdd(available, "child1");
+            Integer child2 = fStateSystem.getQuarkRelativeAndAdd(available, "child2");
+            fStateSystem.modifyAttribute(START_TIME + 10, VALUE, available);
+            fStateSystem.modifyAttribute(START_TIME + 10, VALUE, child1);
+            fStateSystem.modifyAttribute(START_TIME + 10, VALUE, child2);
+
+            pool.recycle(available, START_TIME + 20);
+
+            ITmfStateValue value = fStateSystem.queryOngoingState(available);
+            assertEquals(TmfStateValue.nullValue(), value);
+            value = fStateSystem.queryOngoingState(child1);
+            assertEquals(TmfStateValue.nullValue(), value);
+            value = fStateSystem.queryOngoingState(child2);
+            assertEquals(TmfStateValue.nullValue(), value);
+        } catch (StateValueTypeException | AttributeNotFoundException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    /**
+     * Test pool with priority queue
+     */
+    @Test
+    public void testPriorityPool() {
+        TmfAttributePool pool = new TmfAttributePool(fStateSystem, ITmfStateSystem.ROOT_ATTRIBUTE, QueueType.PRIORITY);
+        assertNotNull(pool);
+
+        /* Get some attributes */
+        Integer available = pool.getAvailable();
+        assertNotNull(available);
+        Integer available2 = pool.getAvailable();
+        assertNotNull(available2);
+        assertNotEquals(available, available2);
+
+        /* Verify the names of the attributes */
+        assertEquals("0", fStateSystem.getAttributeName(available));
+        assertEquals("1", fStateSystem.getAttributeName(available2));
+
+        /* Recycle on */
+        pool.recycle(available, START_TIME + 20);
+
+        /* Get a new one and make sure it is reusing the one just recycled */
+        Integer available3 = pool.getAvailable();
+        assertEquals(available, available3);
+
+        /* Get a new attribute and make sure it is different from both other used attributes */
+        Integer available4 = pool.getAvailable();
+        assertNotEquals(available3, available4);
+        assertNotEquals(available2, available4);
+
+        /* Recycle available attributes, in reverse order and see how they are returned */
+        pool.recycle(available4, START_TIME + 30);
+        pool.recycle(available2, START_TIME + 30);
+        pool.recycle(available3, START_TIME + 30);
+
+        Integer available5 = pool.getAvailable();
+        assertEquals(available3, available5);
+
+        Integer available6 = pool.getAvailable();
+        assertEquals(available2, available6);
+
+        Integer available7 = pool.getAvailable();
+        assertEquals(available4, available7);
+    }
+
+    /**
+     * Test recycling the root attribute
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecycleWrongQuark() {
+        TmfAttributePool pool = new TmfAttributePool(fStateSystem, ITmfStateSystem.ROOT_ATTRIBUTE);
+        assertNotNull(pool);
+
+        pool.recycle(ITmfStateSystem.ROOT_ATTRIBUTE, START_TIME + 10);
+    }
+
+    /**
+     * Test recycling one of the children
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecycleChildQuark() {
+        TmfAttributePool pool = new TmfAttributePool(fStateSystem, ITmfStateSystem.ROOT_ATTRIBUTE);
+        assertNotNull(pool);
+
+        /* Get some attributes */
+        Integer available = pool.getAvailable();
+        assertNotNull(available);
+
+        /* Add children and set values for them */
+        try {
+            Integer child1 = fStateSystem.getQuarkRelativeAndAdd(available, "child1");
+            pool.recycle(child1, START_TIME + 10);
+        } catch (StateValueTypeException e) {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/TmfAttributePool.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/TmfAttributePool.java
new file mode 100644 (file)
index 0000000..309a41f
--- /dev/null
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.core.statesystem;
+
+import java.util.LinkedList;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.tmf.core.Activator;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+
+/**
+ * This class allows to recycle state system attributes. Instead of creating a
+ * lot of short-lived attributes, it is sometimes useful to re-use an attribute
+ * (and its whole sub-tree) that was previously used and is no longer required.
+ * This class keeps a list of children attributes of a base quark and grows that
+ * list as needed.
+ *
+ * @author Geneviève Bastien
+ * @since 2.0
+ */
+public class TmfAttributePool {
+
+    private final ITmfStateSystemBuilder fSs;
+    private final Integer fBaseQuark;
+    private final Queue<@Nullable Integer> fAvailableQuarks;
+    private final Set<Integer> fQuarksInUse = new TreeSet<>();
+    private int fCount = 0;
+
+    /**
+     * The type of queue
+     */
+    public enum QueueType {
+        /**
+         * First In First Out, available attributes are stored and returned in
+         * the order in which they are recycled
+         */
+        FIFO,
+        /**
+         * Available attributes are returned by their number, so attributes with
+         * lower numbers will be used more often
+         */
+        PRIORITY
+    }
+
+    /**
+     * Constructor
+     *
+     * @param ss
+     *            The state system
+     * @param baseQuark
+     *            The base quark under which to add the recyclable attributes
+     */
+    public TmfAttributePool(ITmfStateSystemBuilder ss, Integer baseQuark) {
+        this(ss, baseQuark, QueueType.FIFO);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param ss
+     *            The state system
+     * @param baseQuark
+     *            The base quark under which to add the recyclable attributes
+     * @param type
+     *            The type of queue to use for the attribute pool
+     */
+    public TmfAttributePool(ITmfStateSystemBuilder ss, Integer baseQuark, QueueType type) {
+        fSs = ss;
+        try {
+            /* Make sure the base quark is in range */
+            ss.getParentAttributeQuark(baseQuark);
+            fBaseQuark = baseQuark;
+        } catch (IndexOutOfBoundsException e) {
+            throw new IllegalArgumentException("The quark used as base for the attribute pool does not exist"); //$NON-NLS-1$
+        }
+        switch (type) {
+        case FIFO:
+            fAvailableQuarks = new LinkedList<>();
+            break;
+        case PRIORITY:
+            fAvailableQuarks = new PriorityQueue<>();
+            break;
+        default:
+            throw new IllegalArgumentException("Wrong queue type"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Get an available attribute quark. If there is one available, it will be
+     * reused, otherwise a new quark will be created under the base quark. The
+     * name of the attributes is a sequential integer. So the first quark to be
+     * added will be named '0', the next one '1', etc.
+     *
+     * @return An available quark
+     */
+    public synchronized int getAvailable() {
+        Integer quark = fAvailableQuarks.poll();
+        if (quark == null) {
+            quark = fSs.getQuarkRelativeAndAdd(fBaseQuark, String.valueOf(fCount));
+            fCount++;
+        }
+        fQuarksInUse.add(quark);
+        return quark;
+    }
+
+    /**
+     * Recycle a quark so that it can be reused by calling the
+     * {@link #getAvailable()} method. The quark has to have been obtained from
+     * a previous call to {@link #getAvailable()}. It will set the quark's value
+     * in the state system to a null value.
+     *
+     * It is assumed that it will be reused in the same context each time, so
+     * all children are kept and set to null in this method. The quarks are
+     * still available for the caller, nothing prevents from re-using them
+     * without referring to this class. That means if any attribute's value need
+     * to be non-null after recycling the quark, the caller can do it after
+     * calling this method.
+     *
+     * @param quark
+     *            The quark to recycle.
+     * @param ts
+     *            The timestamp at which to close this attribute.
+     */
+    public synchronized void recycle(int quark, long ts) {
+        if (!fQuarksInUse.remove(quark)) {
+            throw new IllegalArgumentException();
+        }
+        try {
+            fSs.removeAttribute(ts, quark);
+        } catch (AttributeNotFoundException e) {
+            Activator.logError("Error getting sub-attributes", e); //$NON-NLS-1$
+        }
+        fAvailableQuarks.add(quark);
+    }
+
+}
This page took 0.032133 seconds and 5 git commands to generate.