tmf: Make TransientState really re-entrant
authorAlexandre Montplaisir <alexmonthy@voxpopuli.im>
Thu, 23 Jan 2014 23:25:40 +0000 (18:25 -0500)
committerAlexandre Montplaisir <alexmonthy@voxpopuli.im>
Mon, 27 Jan 2014 19:36:10 +0000 (14:36 -0500)
Wrap all reading and writing methods under a reader-writer lock.
This will allow much better consistency when querying a state
system while it is being built.

It was also required to rework the hasInfoAbout() method. Since the
state of the TransientState can change between two method invocations,
it was prone to race conditions. Now it will return the interval it
had at the time, if there is one.

Change-Id: I1a34e410eea1a02288c5388d1983e9c16ce56f0f
Reviewed-on: https://git.eclipse.org/r/21019
Tested-by: Hudson CI
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
IP-Clean: Matthew Khouzam <matthew.khouzam@ericsson.com>
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
IP-Clean: Patrick Tasse <patrick.tasse@gmail.com>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-by: Alexandre Montplaisir <alexmonthy@voxpopuli.im>
org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/statesystem/StateSystem.java
org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/statesystem/TransientState.java

index 769188ef786c943e657b33021b5a818600a12fb5..9420753d548704c6447a4aa0fee398597de5595c 100644 (file)
@@ -562,10 +562,12 @@ public class StateSystem implements ITmfStateSystemBuilder {
             throw new StateSystemDisposedException();
         }
 
-        ITmfStateInterval ret;
-        if (transState.hasInfoAboutStateOf(t, attributeQuark)) {
-            ret = transState.getOngoingInterval(attributeQuark);
-        } else {
+        ITmfStateInterval ret = transState.getIntervalAt(t, attributeQuark);
+        if (ret == null) {
+            /*
+             * The transient state did not have the information, let's look into
+             * the backend next.
+             */
             ret = backend.doSingularQuery(t, attributeQuark);
         }
 
index c86b90641a68fd0f29162c1fd51f2f2436fb541f..7f3cda5bd2769e9685cf2b93fae2c088ba5bf4de 100644 (file)
@@ -15,8 +15,10 @@ package org.eclipse.linuxtools.internal.tmf.core.statesystem;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.IStateHistoryBackend;
 import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
 import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
@@ -44,9 +46,12 @@ public class TransientState {
     /* Indicates where to insert state changes that we generate */
     @NonNull private final IStateHistoryBackend backend;
 
-    private boolean isActive;
-    private long latestTime;
+    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(false);
 
+    private volatile boolean isActive;
+    private volatile long latestTime;
+
+    /* A method accessing these arrays will have to go through the lock */
     private List<ITmfStateValue> ongoingStateInfo;
     private List<Long> ongoingStateStartTimes;
     private List<Type> stateValueTypes;
@@ -86,8 +91,13 @@ public class TransientState {
      *             If the quark is invalid
      */
     public ITmfStateValue getOngoingStateValue(int quark) throws AttributeNotFoundException {
-        checkValidAttribute(quark);
-        return ongoingStateInfo.get(quark);
+        rwl.readLock().lock();
+        try {
+            checkValidAttribute(quark);
+            return ongoingStateInfo.get(quark);
+        } finally {
+            rwl.readLock().unlock();
+        }
     }
 
     /**
@@ -100,8 +110,13 @@ public class TransientState {
      *             If the quark is invalid
      */
     public long getOngoingStartTime(int quark) throws AttributeNotFoundException {
-        checkValidAttribute(quark);
-        return ongoingStateStartTimes.get(quark);
+        rwl.readLock().lock();
+        try {
+            checkValidAttribute(quark);
+            return ongoingStateStartTimes.get(quark);
+        } finally {
+            rwl.readLock().unlock();
+        }
     }
 
     /**
@@ -117,8 +132,13 @@ public class TransientState {
      */
     public void changeOngoingStateValue(int quark, ITmfStateValue newValue)
             throws AttributeNotFoundException {
-        checkValidAttribute(quark);
-        ongoingStateInfo.set(quark, newValue);
+        rwl.writeLock().lock();
+        try {
+            checkValidAttribute(quark);
+            ongoingStateInfo.set(quark, newValue);
+        } finally {
+            rwl.writeLock().unlock();
+        }
     }
 
     /**
@@ -133,9 +153,43 @@ public class TransientState {
      *             If the quark is invalid
      */
     public ITmfStateInterval getOngoingInterval(int quark) throws AttributeNotFoundException {
-        checkValidAttribute(quark);
-        return new TmfStateInterval(ongoingStateStartTimes.get(quark), latestTime,
-                quark, ongoingStateInfo.get(quark));
+        rwl.readLock().lock();
+        try {
+            checkValidAttribute(quark);
+            return new TmfStateInterval(ongoingStateStartTimes.get(quark), latestTime,
+                    quark, ongoingStateInfo.get(quark));
+        } finally {
+            rwl.readLock().unlock();
+        }
+    }
+
+    /**
+     * Try to get the state interval valid for time/quark, if it is present in
+     * this transient state. If it is not (for example, a new value is active
+     * since after the specified timestamp) then null will be returned.
+     *
+     * @param time
+     *            The timestamp to look for
+     * @param quark
+     *            The quark of the attribute to look for
+     * @return The corresponding TmfStateInterval object if we could find it in
+     *         this transient state, or null if we couldn't.
+     */
+    @Nullable
+    public ITmfStateInterval getIntervalAt(long time, int quark) {
+        rwl.readLock().lock();
+        try {
+            checkValidAttribute(quark);
+            if (!isActive() || time < ongoingStateStartTimes.get(quark)) {
+                return null;
+            }
+            return new TmfStateInterval(ongoingStateStartTimes.get(quark),
+                    latestTime, quark, ongoingStateInfo.get(quark));
+        } catch (AttributeNotFoundException e) {
+            return null;
+        } finally {
+            rwl.readLock().unlock();
+        }
     }
 
     private void checkValidAttribute(int quark) throws AttributeNotFoundException {
@@ -155,16 +209,22 @@ public class TransientState {
      *            "ongoing state". Their end times don't matter, we will only
      *            check their value and start times.
      */
-    public synchronized void replaceOngoingState(List<ITmfStateInterval> newStateIntervals) {
-        int size = newStateIntervals.size();
-        ongoingStateInfo = new ArrayList<>(size);
-        ongoingStateStartTimes = new ArrayList<>(size);
-        stateValueTypes = new ArrayList<>(size);
-
-        for (ITmfStateInterval interval : newStateIntervals) {
-            ongoingStateInfo.add(interval.getStateValue());
-            ongoingStateStartTimes.add(interval.getStartTime());
-            stateValueTypes.add(interval.getStateValue().getType());
+    public void replaceOngoingState(List<ITmfStateInterval> newStateIntervals) {
+        final int size = newStateIntervals.size();
+
+        rwl.writeLock().lock();
+        try {
+            ongoingStateInfo = new ArrayList<>(size);
+            ongoingStateStartTimes = new ArrayList<>(size);
+            stateValueTypes = new ArrayList<>(size);
+
+            for (ITmfStateInterval interval : newStateIntervals) {
+                ongoingStateInfo.add(interval.getStateValue());
+                ongoingStateStartTimes.add(interval.getStartTime());
+                stateValueTypes.add(interval.getStateValue().getType());
+            }
+        } finally {
+            rwl.writeLock().unlock();
         }
     }
 
@@ -173,36 +233,22 @@ public class TransientState {
      * Ongoing... tables can stay in sync with the number of attributes in the
      * attribute tree, namely when we add sub-path attributes.
      */
-    public synchronized void addEmptyEntry() {
-        /*
-         * Since this is a new attribute, we suppose it was in the "null state"
-         * since the beginning (so we can have intervals covering for all
-         * timestamps). A null interval will then get added at the first state
-         * change.
-         */
-        ongoingStateInfo.add(TmfStateValue.nullValue());
-        stateValueTypes.add(Type.NULL);
-
-        ongoingStateStartTimes.add(backend.getStartTime());
-    }
+    public void addEmptyEntry() {
+        rwl.writeLock().lock();
+        try {
+            /*
+             * Since this is a new attribute, we suppose it was in the
+             * "null state" since the beginning (so we can have intervals
+             * covering for all timestamps). A null interval will then get added
+             * at the first state change.
+             */
+            ongoingStateInfo.add(TmfStateValue.nullValue());
+            stateValueTypes.add(Type.NULL);
 
-    /**
-     * Ask if the state information about attribute 'quark' at time 'time' is
-     * present in the Builder as it is right now. If it's not, it's either in
-     * the History Tree, or not in the system at all.
-     *
-     * Note that this method does not return the value itself (we don't even
-     * look for it, we can know by just looking at the timestamp)
-     *
-     * @param time
-     *            The timestamp to look for
-     * @param quark
-     *            The quark of the attribute to look for
-     * @return True if the value is present in the Transient State at this
-     *         moment in time, false if it's not
-     */
-    public boolean hasInfoAboutStateOf(long time, int quark) {
-        return (this.isActive() && time >= ongoingStateStartTimes.get(quark));
+            ongoingStateStartTimes.add(backend.getStartTime());
+        } finally {
+            rwl.writeLock().unlock();
+        }
     }
 
     /**
@@ -222,60 +268,65 @@ public class TransientState {
      *             If the state value to be inserted is of a different type of
      *             what was inserted so far for this attribute.
      */
-    public synchronized void processStateChange(long eventTime,
-            ITmfStateValue value, int quark) throws TimeRangeException,
-            AttributeNotFoundException, StateValueTypeException {
+    public void processStateChange(long eventTime, ITmfStateValue value, int quark)
+            throws TimeRangeException, AttributeNotFoundException, StateValueTypeException {
+        rwl.writeLock().lock();
         assert (this.isActive);
 
-        Type expectedSvType = stateValueTypes.get(quark);
-        checkValidAttribute(quark);
+        try {
+            Type expectedSvType = stateValueTypes.get(quark);
+            checkValidAttribute(quark);
 
-        /*
-         * Make sure the state value type we're inserting is the same as the
-         * one registered for this attribute.
-         */
-        if (expectedSvType == Type.NULL) {
-            /*
-             * The value hasn't been used yet, set it to the value
-             * we're currently inserting (which might be null/-1 again).
-             */
-            stateValueTypes.set(quark, value.getType());
-        } else if ((value.getType() != Type.NULL) && (value.getType() != expectedSvType)) {
             /*
-             * We authorize inserting null values in any type of attribute,
-             * but for every other types, it needs to match our expectations!
+             * Make sure the state value type we're inserting is the same as the
+             * one registered for this attribute.
              */
-            throw new StateValueTypeException();
-        }
+            if (expectedSvType == Type.NULL) {
+                /*
+                 * The value hasn't been used yet, set it to the value we're
+                 * currently inserting (which might be null/-1 again).
+                 */
+                stateValueTypes.set(quark, value.getType());
+            } else if ((value.getType() != Type.NULL) && (value.getType() != expectedSvType)) {
+                /*
+                 * We authorize inserting null values in any type of attribute,
+                 * but for every other types, it needs to match our
+                 * expectations!
+                 */
+                throw new StateValueTypeException();
+            }
 
-        /* Update the Transient State's lastestTime, if needed */
-        if (latestTime < eventTime) {
-            latestTime = eventTime;
-        }
+            if (ongoingStateInfo.get(quark).equals(value)) {
+                /*
+                 * This is the case where the new value and the one already
+                 * present in the Builder are the same. We do not need to create
+                 * an interval, we'll just keep the current one going.
+                 */
+                return;
+            }
 
-        if (ongoingStateInfo.get(quark).equals(value)) {
-            /*
-             * This is the case where the new value and the one already present
-             * in the Builder are the same. We do not need to create an
-             * interval, we'll just keep the current one going.
-             */
-            return;
-        }
+            if (ongoingStateStartTimes.get(quark) < eventTime) {
+                /*
+                 * These two conditions are necessary to create an interval and
+                 * update ongoingStateInfo.
+                 */
+                backend.insertPastState(ongoingStateStartTimes.get(quark),
+                        eventTime - 1, /* End Time */
+                        quark, /* attribute quark */
+                        ongoingStateInfo.get(quark)); /* StateValue */
 
-        if (ongoingStateStartTimes.get(quark) < eventTime) {
-            /*
-             * These two conditions are necessary to create an interval and
-             * update ongoingStateInfo.
-             */
-            backend.insertPastState(ongoingStateStartTimes.get(quark),
-                    eventTime - 1, /* End Time */
-                    quark, /* attribute quark */
-                    ongoingStateInfo.get(quark)); /* StateValue */
+                ongoingStateStartTimes.set(quark, eventTime);
+            }
+            ongoingStateInfo.set(quark, value);
+
+            /* Update the Transient State's lastestTime, if needed */
+            if (latestTime < eventTime) {
+                latestTime = eventTime;
+            }
 
-            ongoingStateStartTimes.set(quark, eventTime);
+        } finally {
+            rwl.writeLock().unlock();
         }
-        ongoingStateInfo.set(quark, value);
-        return;
     }
 
     /**
@@ -288,23 +339,25 @@ public class TransientState {
      *            The requested timestamp
      */
     public void doQuery(List<ITmfStateInterval> stateInfo, long t) {
-        ITmfStateInterval interval;
-
-        if (!this.isActive) {
-            return;
-        }
-        assert (stateInfo.size() == ongoingStateInfo.size());
+        rwl.readLock().lock();
+        try {
+            if (!this.isActive) {
+                return;
+            }
+            assert (stateInfo.size() == ongoingStateInfo.size());
 
-        for (int i = 0; i < ongoingStateInfo.size(); i++) {
-            /*
-             * We build a dummy interval with end time = -1 to put in the answer
-             * to the query.
-             */
-            if (this.hasInfoAboutStateOf(t, i)) {
-                interval = new TmfStateInterval(ongoingStateStartTimes.get(i), -1,
-                        i, ongoingStateInfo.get(i));
-                stateInfo.set(i, interval);
+            for (int i = 0; i < ongoingStateInfo.size(); i++) {
+                /*
+                 * We build a dummy interval with end time = -1 to put in the
+                 * answer to the query.
+                 */
+                final ITmfStateInterval interval = getIntervalAt(t, i);
+                if (interval != null) {
+                    stateInfo.set(i, interval);
+                }
             }
+        } finally {
+            rwl.readLock().unlock();
         }
     }
 
@@ -319,35 +372,41 @@ public class TransientState {
      *            change)
      */
     public void closeTransientState(long endTime) {
-        assert (this.isActive);
-
-        for (int i = 0; i < ongoingStateInfo.size(); i++) {
-            if (ongoingStateStartTimes.get(i) > endTime) {
-                /*
-                 * Handle the cases where trace end > timestamp of last state
-                 * change. This can happen when inserting "future" changes.
-                 */
-                continue;
+        rwl.writeLock().lock();
+        try {
+            assert (this.isActive);
+
+            for (int i = 0; i < ongoingStateInfo.size(); i++) {
+                if (ongoingStateStartTimes.get(i) > endTime) {
+                    /*
+                     * Handle the cases where trace end > timestamp of last
+                     * state change. This can happen when inserting "future"
+                     * changes.
+                     */
+                    continue;
+                }
+                try {
+                    backend.insertPastState(ongoingStateStartTimes.get(i),
+                            endTime, /* End Time */
+                            i, /* attribute quark */
+                            ongoingStateInfo.get(i)); /* StateValue */
+
+                } catch (TimeRangeException e) {
+                    /*
+                     * This shouldn't happen, since we control where the
+                     * interval's start time comes from
+                     */
+                    throw new IllegalStateException(e);
+                }
             }
-            try {
-                backend.insertPastState(ongoingStateStartTimes.get(i),
-                        endTime, /* End Time */
-                        i, /* attribute quark */
-                        ongoingStateInfo.get(i)); /* StateValue */
 
-            } catch (TimeRangeException e) {
-                /*
-                 * This shouldn't happen, since we control where the interval's
-                 * start time comes from
-                 */
-                throw new IllegalStateException(e);
-            }
-        }
+            ongoingStateInfo.clear();
+            ongoingStateStartTimes.clear();
+            this.isActive = false;
 
-        ongoingStateInfo.clear();
-        ongoingStateStartTimes.clear();
-        this.isActive = false;
-        return;
+        } finally {
+            rwl.writeLock().unlock();
+        }
     }
 
     /**
This page took 0.030317 seconds and 5 git commands to generate.