mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-02 17:19:37 +00:00
fixing bug 3728 (deadlock) by copying mark passing for waypoint under the lock, then releasing, then calling into deep stacks
Change-Id: Idc28d6c79781e9a174fce8ee8646f64b08b0a11c
This commit is contained in:
1 parent
d64ee6d79c
commit
aa1627e129
2 files changed
+24
-9
No files matched your search
+20
-9
@@ -1,8 +1,10 @@
|
||||
package com.sap.sailing.domain.ranking;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -29,6 +31,7 @@ import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceRankComparator;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public abstract class AbstractRankingMetric implements RankingMetric {
|
||||
private static final long serialVersionUID = -3671039530564696392L;
|
||||
@@ -664,25 +667,33 @@ public abstract class AbstractRankingMetric implements RankingMetric {
|
||||
if (firstAroundMark != null) {
|
||||
result = firstAroundMark;
|
||||
} else {
|
||||
Iterable<MarkPassing> markPassingsForLegStart = getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getFrom());
|
||||
Distance maxWindwardDistanceTraveled = new MeterDistance(Double.MIN_VALUE);
|
||||
Competitor competitorFarthestAlong = null;
|
||||
List<MarkPassing> copyOfMarkPassingsForLegStart = new ArrayList<>();
|
||||
{ // scope the markPassingsForLegStart, so it is no longer used outside this block
|
||||
final Iterable<MarkPassing> markPassingsForLegStart = getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getFrom());
|
||||
// See bug 3728: obtaining lock for mark passings in order for a waypoint before code potentially called from here,
|
||||
// e.g., through getWindwardDistanceTraveled(...), tries to obtain the read lock for mark passings for a competitor can
|
||||
// result in a deadlock. Therefore, we copy the mark passings for the leg start under the lock, then release it again
|
||||
// before calling into a deep stack with getWindwardDistanceTraveled(...) which may well obtain a read lock on
|
||||
// the mark passings for a competitor.
|
||||
getTrackedRace().lockForRead(markPassingsForLegStart);
|
||||
try {
|
||||
for (MarkPassing mp : markPassingsForLegStart) {
|
||||
Util.addAll(markPassingsForLegStart, copyOfMarkPassingsForLegStart);
|
||||
} finally {
|
||||
getTrackedRace().unlockAfterRead(markPassingsForLegStart);
|
||||
}
|
||||
}
|
||||
Distance maxWindwardDistanceTraveled = new MeterDistance(Double.MIN_VALUE);
|
||||
Competitor competitorFarthestAlong = null;
|
||||
for (MarkPassing mp : copyOfMarkPassingsForLegStart) {
|
||||
if (mp.getTimePoint().after(timePoint)) {
|
||||
break;
|
||||
}
|
||||
final Distance windwardDistanceTraveled = getWindwardDistanceTraveled(mp.getCompetitor(), mp.getWaypoint(),
|
||||
timePoint, cache);
|
||||
final Distance windwardDistanceTraveled = getWindwardDistanceTraveled(mp.getCompetitor(), mp.getWaypoint(), timePoint, cache);
|
||||
if (windwardDistanceTraveled.compareTo(maxWindwardDistanceTraveled) > 0) {
|
||||
maxWindwardDistanceTraveled = windwardDistanceTraveled;
|
||||
competitorFarthestAlong = mp.getCompetitor();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
getTrackedRace().unlockAfterRead(markPassingsForLegStart);
|
||||
}
|
||||
result = competitorFarthestAlong;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -414,6 +414,10 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
* first obtains the mark passings read lock and later, while holding on to that lock, asks for the course's read
|
||||
* lock, a deadlock may result.<p>
|
||||
*
|
||||
* Furthermore, when trying to acquire both, a lock for the {@link #getMarkPassings(Competitor) mark passings for a competitor}
|
||||
* and a lock for the {@link #getMarkPassingsInOrder(Waypoint) mark passings for a waypoint, this needs to happen in exactly
|
||||
* this order, or a deadlock may result.<p>
|
||||
*
|
||||
* The {@link #unlockAfterRead(Iterable)} method will symmetrically unlock the course's read lock after releasing the
|
||||
* read lock for the mark passings.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user