Improve thread-safety of buffer recycling
See original GitHub issue(note: follow-up to #476)
ThreadLocal
based buffer recycling worked well for traditional blocking i/o where usage of a given parser or generator is usually (although not always!) limited to just a single blocking thread.
And although in theory it would be possible to have systems that use sequence of threads for processing (for example first worked thread processing part of heard), we have observed no actual use cases.
However, with async/non-blocking parsing, it is more likely that something similar occurs, in which – for example – one thread might process events for a single buffer-full of content.
This is problematic not because multiple threads try to access explicitly shared resource – they won’t – but because ownership model gets twisted due to ThreadLocal
access by parsers, generators, so that effectively BufferRecycler
that should be “owned” by just one parser / generator at any given point in time may end up getting actually shared. And at this point lack of synchronization (which was assumed not needed due to ThreadLocal
forcing access from just one thread – not true if parser/generator accessed from multiple!) will allow sharing of actual underlying buffers.
We can solve this in multiple ways, but probably the first attempt should be changing array-access of BufferRecycler
fields to use atomic accessors. This adds some overhead, which is likely negligible (only lock when obtain, release), although will probably reduce efficiency of actual recycling more. Still, it should be more efficient than no recycling, and actually safe, as opposed to corruption due to lack of sync.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:5
- Comments:7 (4 by maintainers)
Top GitHub Comments
So, for 2.10, replaced unsynchronized access with
java.util.concurrent.atomic.AtomicReferenceArray
– a very simple change, and unlikely to have measurable negative performance effect(s) (and from casual run ofjackson-benchmarks
for reading, saw absolutely no effect). With that, enabled recycling for async/non-blocking parser again.Also got rid of all recycling for
JsonStringEncoder
(since it’s only used for gettingSerializedString
). For 3.0 (and 2.11 if we do that) will probably try to add fully pluggable abstraction for fetchingBufferRecycler
s using whatever mechanism, could be pooling and notThreadLocal
. Or, if somehow I end up having extra time might even still squeeze in 2.10 but only if it gets in apr
for thorough vetting.@sdeleuze I am happy to be able to reduce some of unnecessary complexity! 2.10 will also reduce some of maximum recyclable buffer sizes which can help reduce memory retention (per-thread) quite a bit.