-
-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[cache] Fix concurrent use of a cache entry #3785
Conversation
Closes openhab#3507 Fix some rare case where the concurrent readings of a cache entry can fail. Signed-off-by: Gwendal Roulleau <gwendal.roulleau@gmail.com>
Thanks for having a look! Wouldn't there also concurrency issues when multiple InputStreams call There are also issues when protected Long getTotalSize() {
if (completed) { // we already know the total size of the sound
return currentSize; |
I don't think so ? Multiple threads can call this "read" method concurrently :
I think currentSize cannot be updated concurrently with completed. They are exclusive (one is in IF / the other is in ELSE) and in a synchronized block with "! completed" as a condition to enter the block. But concurrent operations are tricky, I may have missed something... |
I now see that threads won't go into the while loop once
Why would this matter if the statements are executed sequentially? Is there something asynchronous going on in the FileChannel? |
OK, I was not very clear, sorry. Imagine 2 threads that want the same cache entry. Neither of them had read any data, and its a cache miss, so the cache entry is empty.
By the way, for maxToRead, using a min between sizeToRead and FileChannel.size is needed because we can imagine a file smaller than the sizeToRead asked by the caller. Allocating the exact data size needed is mandatory to fill it and returns exactly the right amount of data read from the FileChannel, without reallocating another buffer inbetween. So, using currentSize to compute the size of the buffer we will fill was wrong, in some rare instances there was some concurrency issues. Using FileChannel.size fixes that. This system is complex but I'm a little bit proud of it: with it, we can cache AND serve data to several thread requesting it concurrently, without waiting for the end of the source input stream. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed explanation! I now understand why it happens and did not think about a thread being suspended while it is running in the synchronized block. 🙃 It's certainly worth it to have no delays. In the end the unit test also helped with finding a real concurrency issue. 🙂
Closes #3507
Fix some rare case where the concurrent reads of a cache entry can fail.
Explanation:
Lines 286 and 288 are executed nearly at the same time. "Nearly" was sufficient to cause error when setting maxToRead and inducing the thread to think that the wanted data cannot be read.