@@ -1645,7 +1645,56 @@ public void addPointToTrace(ITrace2D trace, double xData, double yData){
16451645 }
16461646 trace .addPoint (xData , yData );
16471647 }
1648-
1648+
1649+ /** DEV-896: Max points added per single chart-monitor acquisition in {@link #addPointsToTrace}.
1650+ * Bounds how long the batch can hold the chart lock so a large burst can't monopolise the EDT. */
1651+ private static final int POINT_BATCH_MAX = 256 ;
1652+
1653+ /**
1654+ * DEV-896: Batch variant of {@link #addPointToTrace(ITrace2D, double, double)}. Adds many
1655+ * points to a single trace while acquiring the chart monitor once per (bounded) chunk rather
1656+ * than once per point. {@code ATrace2D.addPoint()} synchronizes on the chart
1657+ * ({@code trace.getRenderer()}) - the same monitor {@code Chart2D.paintComponent()} holds - so
1658+ * adding N points individually took the monitor N times and starved the Swing EDT. Java monitors
1659+ * are reentrant, so {@code addPoint()}'s internal {@code synchronized(chart)} is free while we
1660+ * hold the outer lock. Behaviour per point is identical to {@link #addPointToTrace}.
1661+ */
1662+ public void addPointsToTrace (ITrace2D trace , double [] xData , double [] yData ){
1663+ if (xData == null || yData == null ){
1664+ return ;
1665+ }
1666+ addPointsToTrace (trace , xData , yData , 0 , Math .min (xData .length , yData .length ));
1667+ }
1668+
1669+ /**
1670+ * DEV-896: See {@link #addPointsToTrace(ITrace2D, double[], double[])}. Adds points
1671+ * {@code [fromIndex, toIndex)} from the given arrays.
1672+ */
1673+ public void addPointsToTrace (ITrace2D trace , double [] xData , double [] yData , int fromIndex , int toIndex ){
1674+ if (trace == null || xData == null || yData == null ){
1675+ return ;
1676+ }
1677+ int end = Math .min (toIndex , Math .min (xData .length , yData .length ));
1678+ int i = Math .max (0 , fromIndex );
1679+ //trace.getRenderer() returns the Chart2D the trace was added to (null until then); it is the
1680+ //exact monitor ATrace2D.addPoint() locks, so holding it makes the per-point locks reentrant.
1681+ Chart2D chart = trace .getRenderer ();
1682+ while (i < end ){
1683+ int chunkEnd = Math .min (i + POINT_BATCH_MAX , end );
1684+ if (chart != null ){
1685+ synchronized (chart ){
1686+ for (; i < chunkEnd ; i ++){
1687+ addPointToTrace (trace , xData [i ], yData [i ]);
1688+ }
1689+ }
1690+ } else {
1691+ for (; i < chunkEnd ; i ++){
1692+ addPointToTrace (trace , xData [i ], yData [i ]);
1693+ }
1694+ }
1695+ }
1696+ }
1697+
16491698 public CircularFifoBuffer getCirculurBufferedTraceData (String traceName ){
16501699 CircularFifoBuffer circularFifoBuffer = mMapOfCirculurBufferedTraceDataPoints .get (traceName );
16511700 if (circularFifoBuffer != null ){
@@ -1869,10 +1918,9 @@ public void run() {
18691918 if (results .length ==2 && results [0 ].length >startBin ){
18701919
18711920 trace .removeAllPoints ();
1872-
1873- for (int x =startBin ;x <results [0 ].length ;x ++){
1874- addPointToTrace (trace , results [0 ][x ], results [1 ][x ]);
1875- }
1921+
1922+ //DEV-896: batch the FFT bins into one chart-monitor acquisition per chunk
1923+ addPointsToTrace (trace , results [0 ], results [1 ], startBin , results [0 ].length );
18761924 }
18771925 }
18781926
@@ -1951,8 +1999,17 @@ public void filterDataAndPlot(ObjectCluster ojc) throws Exception {
19511999 synchronized (mListofPropertiestoPlot ){
19522000 Iterator <String []> entries = mListofPropertiestoPlot .iterator ();
19532001 int indexOfTrace = 0 ;
1954- boolean isDummyPointAddedToFillTrace = false ;
1955-
2002+ boolean isDummyPointAddedToFillTrace = false ;
2003+
2004+ //DEV-896: Acquire the chart monitor once for this whole multi-trace update instead
2005+ //of once per trace inside ATrace2D.addPoint(). addPoint() synchronizes on the chart
2006+ //(trace.getRenderer()) - the same monitor Chart2D.paintComponent() holds - so grabbing
2007+ //it once per sample was starving the Swing EDT. Java monitors are reentrant, so the
2008+ //per-point synchronized(chart) inside addPoint() is free while we hold this outer lock.
2009+ //The batch is bounded by the number of plotted traces, so the lock hold stays short.
2010+ //Falls back to the already-held mListofPropertiestoPlot monitor if no chart is set yet.
2011+ Object chartMonitor = (mChart != null ) ? (Object )mChart : (Object )mListofPropertiestoPlot ;
2012+ synchronized (chartMonitor ){
19562013 while (entries .hasNext ()) {
19572014 String [] props = entries .next ();
19582015
@@ -2067,6 +2124,7 @@ else if(isXAxisFrequency){
20672124 }
20682125 indexOfTrace ++;
20692126 }
2127+ } //DEV-896: release the chart monitor once the whole multi-trace update is done
20702128 if (isDummyPointAddedToFillTrace ) {
20712129 isFirstPointOnFillTrace = false ;
20722130 }
0 commit comments