From d1277441405dbb29b720dc6cfc21ce21a126aee2 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Wed, 8 Jul 2026 17:05:36 +0300 Subject: [PATCH 1/2] Stop shadowing Task._stop and Task._lastException (CodeQL java/field-masks-super-field) StreamLoader and StreamSaver each redeclared _stop and _lastException, which are already declared in their superclass Task. The shadowing meant the two classes did not share the base fields, breaking the Task framework's contract: - Task._stop is an AtomicBoolean that Task.stop() sets to request cancellation, but StreamSaver declared its own boolean _stop that nothing ever set, so its save loops (saveAll / saveTrees / the traversal loop) never observed a stop request and could not be cancelled. Remove the shadow field and test the inherited AtomicBoolean via _stop.get(). - Task._lastException is the field Task reports through the task status, but StreamSaver recorded failures into its own shadow copy, so the reported status stayed "none". Remove the shadow so the recorded exception reaches the status. - StreamLoader's _stop and _lastException were shadow fields that were never read or written, so they are simply removed. For a normal (non-cancelled) run _stop is false throughout, so the save loops behave exactly as before; the change only lets a cancellation actually take effect and lets the recorded exception surface in the task status. --- .../core/src/main/java/com/persistit/StreamLoader.java | 3 +-- .../core/src/main/java/com/persistit/StreamSaver.java | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/persistit/core/src/main/java/com/persistit/StreamLoader.java b/persistit/core/src/main/java/com/persistit/StreamLoader.java index f21f63da1b..d51460065b 100644 --- a/persistit/core/src/main/java/com/persistit/StreamLoader.java +++ b/persistit/core/src/main/java/com/persistit/StreamLoader.java @@ -12,6 +12,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * Portions Copyrighted 2026 3A Systems, LLC */ package com.persistit; @@ -50,8 +51,6 @@ public class StreamLoader extends Task { protected Tree _lastTree; protected int _dataRecordCount = 0; protected int _otherRecordCount = 0; - protected boolean _stop; - protected Exception _lastException; protected TreeSelector _treeSelector; protected boolean _createMissingVolumes; diff --git a/persistit/core/src/main/java/com/persistit/StreamSaver.java b/persistit/core/src/main/java/com/persistit/StreamSaver.java index 5d2b3a77fe..1a48dda16c 100644 --- a/persistit/core/src/main/java/com/persistit/StreamSaver.java +++ b/persistit/core/src/main/java/com/persistit/StreamSaver.java @@ -115,8 +115,6 @@ public class StreamSaver extends Task { protected long _dataRecordCount = 0; protected long _otherRecordCount = 0; protected int _cycleCount = DEFAULT_CYCLE_COUNT; - protected boolean _stop; - protected Exception _lastException; protected int _recordCount; protected TreeSelector _treeSelector; @@ -244,7 +242,7 @@ public void setCycleCount(final int count) { */ public void close() throws IOException { writeTimestamp(); - if (!_stop && _lastException == null) + if (!_stop.get() && _lastException == null) _dos.writeChar(RECORD_TYPE_COMPLETION); _lastTree = null; _lastVolume = null; @@ -456,7 +454,7 @@ public void save(final Exchange exchange, final KeyFilter filter) throws Persist } final Key key = exchange.getKey(); key.clear().append(Key.BEFORE); - while (exchange.traverse(Key.GT, filter, Integer.MAX_VALUE) & !_stop) { + while (exchange.traverse(Key.GT, filter, Integer.MAX_VALUE) & !_stop.get()) { writeData(exchange); } writeRecordCount(_dataRecordCount, _otherRecordCount); @@ -497,7 +495,7 @@ public void saveTrees(final String volumeName, final String[] selectedTreeNames) public void saveTrees(final Volume volume, final String[] selectedTreeNames) throws PersistitException, IOException { final String[] treeNames = volume.getTreeNames(); writeComment("Volume " + volume.getPath()); - for (int index = 0; index < treeNames.length & !_stop; index++) { + for (int index = 0; index < treeNames.length & !_stop.get(); index++) { boolean selected = true; if (selectedTreeNames != null) { for (int index2 = 0; selected && index2 < selectedTreeNames.length; index2++) { @@ -567,7 +565,7 @@ public void saveTrees(final TreeSelector treeSelector) throws PersistitException public void saveAll() throws PersistitException, IOException { for (final Volume volume : _persistit.getVolumes()) { - if (_stop) { + if (_stop.get()) { break; } saveTrees(volume, null); From 183d5c72037aa81964cdeeccc840fc544b63e6a2 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Wed, 8 Jul 2026 17:23:47 +0300 Subject: [PATCH 2/2] Use short-circuit && for stop checks in StreamSaver (CodeQL java/non-short-circuit-evaluation) The save loops combined the traversal/index test with the stop flag using the non-short-circuit & operator. The right operand (!_stop.get()) has no side effects and the left operand is always evaluated first, so switching to && is behaviour-preserving and avoids the redundant _stop.get() once the loop condition's left side is already false. --- persistit/core/src/main/java/com/persistit/StreamSaver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/persistit/core/src/main/java/com/persistit/StreamSaver.java b/persistit/core/src/main/java/com/persistit/StreamSaver.java index 1a48dda16c..76d92ce5d4 100644 --- a/persistit/core/src/main/java/com/persistit/StreamSaver.java +++ b/persistit/core/src/main/java/com/persistit/StreamSaver.java @@ -454,7 +454,7 @@ public void save(final Exchange exchange, final KeyFilter filter) throws Persist } final Key key = exchange.getKey(); key.clear().append(Key.BEFORE); - while (exchange.traverse(Key.GT, filter, Integer.MAX_VALUE) & !_stop.get()) { + while (exchange.traverse(Key.GT, filter, Integer.MAX_VALUE) && !_stop.get()) { writeData(exchange); } writeRecordCount(_dataRecordCount, _otherRecordCount); @@ -495,7 +495,7 @@ public void saveTrees(final String volumeName, final String[] selectedTreeNames) public void saveTrees(final Volume volume, final String[] selectedTreeNames) throws PersistitException, IOException { final String[] treeNames = volume.getTreeNames(); writeComment("Volume " + volume.getPath()); - for (int index = 0; index < treeNames.length & !_stop.get(); index++) { + for (int index = 0; index < treeNames.length && !_stop.get(); index++) { boolean selected = true; if (selectedTreeNames != null) { for (int index2 = 0; selected && index2 < selectedTreeNames.length; index2++) {