-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
gh-116738: Make lzma module thread-safe #142947
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Make the attributes in :mod:`lzma` thread-safe on the :term:`free threaded | ||
| <free threading>` build. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |||||||||||||||||||||
| #include "pycore_long.h" // _PyLong_UInt32_Converter() | ||||||||||||||||||||||
| // Blocks output buffer wrappers | ||||||||||||||||||||||
| #include "pycore_blocks_output_buffer.h" | ||||||||||||||||||||||
| #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_*_RELAXED | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #if OUTPUT_BUFFER_MAX_BLOCK_SIZE > SIZE_MAX | ||||||||||||||||||||||
| #error "The maximum block size accepted by liblzma is SIZE_MAX." | ||||||||||||||||||||||
|
|
@@ -948,10 +949,10 @@ decompress_buf(Decompressor *d, Py_ssize_t max_length) | |||||||||||||||||||||
| goto error; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (lzret == LZMA_GET_CHECK || lzret == LZMA_NO_CHECK) { | ||||||||||||||||||||||
| d->check = lzma_get_check(&d->lzs); | ||||||||||||||||||||||
| FT_ATOMIC_STORE_INT_RELAXED(d->check, lzma_get_check(&d->lzs)); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (lzret == LZMA_STREAM_END) { | ||||||||||||||||||||||
| d->eof = 1; | ||||||||||||||||||||||
| FT_ATOMIC_STORE_CHAR_RELAXED(d->eof, 1); | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| } else if (lzs->avail_out == 0) { | ||||||||||||||||||||||
| /* Need to check lzs->avail_out before lzs->avail_in. | ||||||||||||||||||||||
|
|
@@ -1038,7 +1039,7 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length) | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (d->eof) { | ||||||||||||||||||||||
| d->needs_input = 0; | ||||||||||||||||||||||
| FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); | ||||||||||||||||||||||
| if (lzs->avail_in > 0) { | ||||||||||||||||||||||
| Py_XSETREF(d->unused_data, | ||||||||||||||||||||||
| PyBytes_FromStringAndSize((char *)lzs->next_in, lzs->avail_in)); | ||||||||||||||||||||||
|
|
@@ -1054,17 +1055,17 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length) | |||||||||||||||||||||
| /* (avail_in==0 && avail_out==0) | ||||||||||||||||||||||
| Maybe lzs's internal state still have a few bytes can | ||||||||||||||||||||||
| be output, try to output them next time. */ | ||||||||||||||||||||||
| d->needs_input = 0; | ||||||||||||||||||||||
| FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* If max_length < 0, lzs->avail_out always > 0 */ | ||||||||||||||||||||||
| assert(max_length >= 0); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| /* Input buffer exhausted, output buffer has space. */ | ||||||||||||||||||||||
| d->needs_input = 1; | ||||||||||||||||||||||
| FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 1); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| else { | ||||||||||||||||||||||
| d->needs_input = 0; | ||||||||||||||||||||||
| FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* If we did not use the input buffer, we now have | ||||||||||||||||||||||
| to copy the tail from the caller's buffer into the | ||||||||||||||||||||||
|
|
@@ -1314,15 +1315,32 @@ PyDoc_STRVAR(Decompressor_needs_input_doc, | |||||||||||||||||||||
| PyDoc_STRVAR(Decompressor_unused_data_doc, | ||||||||||||||||||||||
| "Data found after the end of the compressed stream."); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| static PyObject * | ||||||||||||||||||||||
| Decompressor_unused_data_get(PyObject *op, void *Py_UNUSED(ignored)) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| Decompressor *self = Decompressor_CAST(op); | ||||||||||||||||||||||
| PyMutex_Lock(&self->mutex); | ||||||||||||||||||||||
| PyObject *result = Py_XNewRef(self->unused_data); | ||||||||||||||||||||||
| PyMutex_Unlock(&self->mutex); | ||||||||||||||||||||||
| if (result == NULL) { | ||||||||||||||||||||||
| PyErr_SetString(PyExc_AttributeError, "unused_data"); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+1325
to
+1327
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think we should probably initialize
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One other possibility would be to standardize on the implementation in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you @emmatyping for pointing this out. I actually wanted to Lines 1040 to 1049 in 08bc03f
So, how do you think we should change it? |
||||||||||||||||||||||
| return result; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| static PyGetSetDef Decompressor_getset[] = { | ||||||||||||||||||||||
| {"unused_data", Decompressor_unused_data_get, NULL, | ||||||||||||||||||||||
| Decompressor_unused_data_doc}, | ||||||||||||||||||||||
| {NULL}, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| static PyMemberDef Decompressor_members[] = { | ||||||||||||||||||||||
| {"check", Py_T_INT, offsetof(Decompressor, check), Py_READONLY, | ||||||||||||||||||||||
| Decompressor_check_doc}, | ||||||||||||||||||||||
| {"eof", Py_T_BOOL, offsetof(Decompressor, eof), Py_READONLY, | ||||||||||||||||||||||
| Decompressor_eof_doc}, | ||||||||||||||||||||||
| {"needs_input", Py_T_BOOL, offsetof(Decompressor, needs_input), Py_READONLY, | ||||||||||||||||||||||
| Decompressor_needs_input_doc}, | ||||||||||||||||||||||
| {"unused_data", Py_T_OBJECT_EX, offsetof(Decompressor, unused_data), Py_READONLY, | ||||||||||||||||||||||
| Decompressor_unused_data_doc}, | ||||||||||||||||||||||
| {NULL} | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -1332,6 +1350,7 @@ static PyType_Slot lzma_decompressor_type_slots[] = { | |||||||||||||||||||||
| {Py_tp_new, _lzma_LZMADecompressor}, | ||||||||||||||||||||||
| {Py_tp_doc, (char *)_lzma_LZMADecompressor__doc__}, | ||||||||||||||||||||||
| {Py_tp_members, Decompressor_members}, | ||||||||||||||||||||||
| {Py_tp_getset, Decompressor_getset}, | ||||||||||||||||||||||
| {0, 0} | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.