Bug #6161
closednet.sf.saxon.tree.iter.ReportingSingletonIterator.next throws NullPointerException - race condition
100%
Description
In our test environment, we recently started using Saxon-EE 12.3. During one of our transformations, we got the following error message:
2023-08-08 17:12:36 [main] Launching /tph/delete-obsolete-trains.xslt
2023-08-08 17:12:36 [main] parameter new-file=/var/opt/app/arte2/envs/u00t/interfac/exporter/tph/train.xml
2023-08-08 17:12:36 [main] parameter old-file=/var/opt/app/arte2/envs/u00t/interfac/exporter/tph/previous/train.xml
2023-08-08 17:12:36 [main] parameter output-folder=/var/opt/app/arte2/envs/u00t/interfac/exporter/tph/run/train-to-delete/
2023-08-08 17:12:36 [main] parameter working-folder=/var/opt/app/arte2/envs/u00t/interfac/exporter/tph/train/
2023-08-08 17:12:36 [main] Deleting /var/opt/app/arte2/envs/u00t/interfac/exporter/tph/train/train_EM_2411.xml
2023-08-08 17:12:36 [main] Deleting /var/opt/app/arte2/envs/u00t/interfac/exporter/tph/train/train_ME_2432.xml
2023-08-08 17:12:36 [main] Deleting /var/opt/app/arte2/envs/u00t/interfac/exporter/tph/train/train_EL_2613.xml
...
2023-08-08 17:12:36 [main] Deleting /var/opt/app/arte2/envs/u00t/interfac/exporter/tph/train/train_ZL_42843.xml
java.lang.NullPointerException
at net.sf.saxon.tree.iter.ReportingSingletonIterator.next(ReportingSingletonIterator.java:42)
at net.sf.saxon.expr.Atomizer.getAtomizingIterator(Atomizer.java:596)
at net.sf.saxon.expr.Atomizer$AtomizerElaborator.lambda$elaborateForPull$0(Atomizer.java:677)
at net.sf.saxon.expr.UntypedSequenceConverter$UntypedSequenceConverterElaborator.lambda$elaborateForPull$0(UntypedSequenceConverter.java:304)
at net.sf.saxon.expr.CardinalityChecker$CardinalityCheckerElaborator.lambda$elaborateForPull$0(CardinalityChecker.java:502)
at net.sf.saxon.expr.elab.SharedAppendEvaluator.lambda$new$1(SharedAppendEvaluator.java:42)
at net.sf.saxon.expr.elab.SharedAppendEvaluator.evaluate(SharedAppendEvaluator.java:63)
at net.sf.saxon.expr.SystemFunctionCall$SystemFunctionCallElaborator.lambda$elaborateForPull$1(SystemFunctionCall.java:605)
at net.sf.saxon.value.SingletonClosure.asItem(SingletonClosure.java:111)
at net.sf.saxon.value.SingletonClosure.materialize(SingletonClosure.java:162)
at net.sf.saxon.expr.elab.LocalVariableEvaluator.evaluate(LocalVariableEvaluator.java:31)
at net.sf.saxon.expr.SystemFunctionCall$SystemFunctionCallElaborator.lambda$elaborateForItem$5(SystemFunctionCall.java:652)
at net.sf.saxon.expr.AtomicSequenceConverter$AtomicSequenceConverterElaborator.lambda$elaborateForItem$1(AtomicSequenceConverter.java:534)
at net.sf.saxon.expr.LetExpression$LetExprElaborator.lambda$elaborateForItem$7(LetExpression.java:957)
at net.sf.saxon.expr.elab.PullElaborator.lambda$elaborateForUnicodeString$3(PullElaborator.java:76)
at net.sf.saxon.expr.instruct.ResultDocument.processLeft(ResultDocument.java:461)
at net.sf.saxon.expr.instruct.ResultDocument.processInstruction(ResultDocument.java:400)
at com.saxonica.config.EnterpriseConfiguration.lambda$processResultDocument$1(EnterpriseConfiguration.java:1926)
at java.util.concurrent.FutureTask.run(FutureTask.java:277)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:522)
at java.util.concurrent.FutureTask.run(FutureTask.java:277)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.lang.Thread.run(Thread.java:825)
Please find attached the transformation in question. Note that a 2nd run gave no problems. Hopefully the above logging can help improve your code.
Files
Updated by Michael Kay over 1 year ago
Thanks for reporting it. This is part of the new mechanism for learning whether a particular expression should be evaluated eagerly or lazily, and guess there could well be a thread-safety issue with it.
Updated by Michael Kay over 1 year ago
- Tracker changed from Support to Bug
- Category set to Multithreading
- Status changed from New to In Progress
- Assignee set to Michael Kay
- Priority changed from Low to Normal
- Applies to branch 12, trunk added
- Platforms .NET, Java added
Updated by Michael Kay over 1 year ago
- Status changed from In Progress to Resolved
I haven't been able to construct a repro, but from studying the code, I think that synchronizing SingletonClosure.asItem()
should be sufficient.
The method wasn't synchronized in 11.x, but I think the worst that could happen then would be that the value of a variable would be evaluated twice, which would normally be symptomless. We could synchronise a smaller block of code in 12.x to retain that behaviour, but I think that synchronizing the whole method makes more sense.
I'm puzzled by another aspect of the code, namely why do we give the SingletonClosure
a PullEvaluator
rather than an ItemEvaluator
over the input expression? The only justification seems to be that it enables inheritance from the abstract class Closure
and therefore a small amount of code reuse. Let's leave well alone on that one.
Updated by O'Neil Delpratt about 1 year ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in Maintenance Release 12.4 added
Bug fix applied in the Saxon 12.4 maintenance release
Please register to edit this issue