Read entries of one phar from several coroutines - #290
Merged
Merged
Conversation
Every compressed entry of an archive is decompressed into one stream shared by the archive, at the offset the entry takes from that stream before it starts writing. Every call of that span parks, so two coroutines opening two compressed entries interleaved: the second appended its own decompression filter to the same write chain and wrote at the offset the first had remembered. Three entries of four came back as "internal corruption of phar ... filesize mismatch" on every run. The fix is in php-src 353ccf7b070, which holds the span as one unit and keeps an archive reference across it. The test covers both halves: four coroutines decompressing four entries, and a stored entry seeked from one coroutine while another decompresses.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…pression # Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #283.
Every compressed entry of an archive is decompressed into
phar->ufp, one stream shared by every entry of that archive, at the offset the entry takes from it before it starts writing. Every call of that span parks — seek to the end, remember the offset, append the decompression filter, copy from the archive stream, flush, remove the filter — so two coroutines opening two compressed entries interleaved: the second appended its own filter to the same write chain and put its bytes at the offset the first had remembered. Four gzipped entries read by four coroutines answeredinternal corruption of phar "..." (actual filesize mismatch on file "...")for three of the four, on every run.The engine side is php-src
353ccf7b070(true-async, merged intotrue-async-stable). It holds the span as one unit: both sides of the archive stream around it and both sides of the uncompressed-file stream inside them, taken write-side first, the orderphp_stream_seek()and the rest of the stream layer take them. The archive stream is remembered once instead of read again after each park, and the entry is examined a second time under the lock, since another coroutine may have opened it while this one queued.The call also holds an archive reference:
phar_get_entry_data()takes its own only afterphar_open_entry_fp()has returned, and without one the coroutine that finished first dropped the last reference andphar_archive_delref()closed the archive stream the others were queued on. The count is restored by hand — a teardown at zero closes the stream the caller is about to read — so a teardown due while the call was parked falls to whoever drops the count to zero next.Reading one archive is serialized per archive as a result, stored entries included.
This PR carries the test and the changelog entry.
tests/io/101-phar_entries_decompressed_at_once.phpt: three broken entries of four before the engine change, 10 green runs of 10 after.ext/phar/tests: 565 of 565.ext/async/testsplus php-srcfile,streams,filters,zlibandphar: 3221 tests, 3 failures, all three the local symlink artefacts (curl 063, curl 064, io 082).Left open, found while reviewing this change and not caused by it:
phar_open_entry_fp()does not hold the entry itself across a park, sophar_entry_remove()from another coroutine can free thephar_entry_infounder it; and two coroutines can both open the archive stream, sincephar_open_archive_fp()does not re-check afterphp_stream_open_wrapper()parks.