gh-133885: Disallow sharing zstd (de)compressor contexts by emmatyping · Pull Request #134253 · python/cpython · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 11 additions & 40 deletions Lib/test/test_zstd.py
17 changes: 17 additions & 0 deletions Modules/_zstd/_zstdmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,21 @@ extern void
set_parameter_error(const _zstd_state* const state, int is_compress,
int key_v, int value_v);

static inline int
check_object_shared(PyObject *ob, char *type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is probably not be the right way to implement this check? Access from different threads could happen regardless of free-threading builds.

I think the concept the extension module might want to enforce is something like getting the current thread id (we have a python c api for this IIRC) upon first "use" of the object, saving that in the object state, and checking that current thread id == that thread id for subsequent uses.

first use could be construction... but unless the zstandard C API requires that, it may be better to consider first use the first time that ZStdCompressor is actually used by a C API. This allows for the pattern of:

A compressor or decompressor is created by one thread - and added to a thread pool or work queue to be actually used and exhausted - exclusively - in another thread.

{
#if defined(Py_GIL_DISABLED)
if (!_Py_IsOwnedByCurrentThread(ob))
{
PyErr_Format(PyExc_RuntimeError,
"%s cannot be shared across multiple threads.",
type);
return 1;
}
return 0;
#else
return 0;
#endif
}

#endif // !ZSTD_MODULE_H
19 changes: 12 additions & 7 deletions Modules/_zstd/compressor.c
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,12 @@ _zstd_ZstdCompressor_compress_impl(ZstdCompressor *self, Py_buffer *data,
{
PyObject *ret;

/* Check we are on the same thread as the compressor was created */
if (check_object_shared((PyObject *)self, "ZstdCompressor") > 0)
{
return NULL;
}

/* Check mode value */
if (mode != ZSTD_e_continue &&
mode != ZSTD_e_flush &&
Expand All @@ -587,9 +593,6 @@ _zstd_ZstdCompressor_compress_impl(ZstdCompressor *self, Py_buffer *data,
return NULL;
}

/* Thread-safe code */
Py_BEGIN_CRITICAL_SECTION(self);

/* Compress */
if (self->use_multithread && mode == ZSTD_e_continue) {
ret = compress_mt_continue_impl(self, data);
Expand All @@ -607,7 +610,6 @@ _zstd_ZstdCompressor_compress_impl(ZstdCompressor *self, Py_buffer *data,
/* Resetting cctx's session never fail */
ZSTD_CCtx_reset(self->cctx, ZSTD_reset_session_only);
}
Py_END_CRITICAL_SECTION();

return ret;
}
Expand All @@ -632,6 +634,12 @@ _zstd_ZstdCompressor_flush_impl(ZstdCompressor *self, int mode)
{
PyObject *ret;

/* Check we are on the same thread as the compressor was created */
if (check_object_shared((PyObject *)self, "ZstdCompressor") > 0)
{
return NULL;
}

/* Check mode value */
if (mode != ZSTD_e_end && mode != ZSTD_e_flush) {
PyErr_SetString(PyExc_ValueError,
Expand All @@ -641,8 +649,6 @@ _zstd_ZstdCompressor_flush_impl(ZstdCompressor *self, int mode)
return NULL;
}

/* Thread-safe code */
Py_BEGIN_CRITICAL_SECTION(self);
ret = compress_impl(self, NULL, mode);

if (ret) {
Expand All @@ -654,7 +660,6 @@ _zstd_ZstdCompressor_flush_impl(ZstdCompressor *self, int mode)
/* Resetting cctx's session never fail */
ZSTD_CCtx_reset(self->cctx, ZSTD_reset_session_only);
}
Py_END_CRITICAL_SECTION();

return ret;
}
Expand Down
15 changes: 11 additions & 4 deletions Modules/_zstd/decompressor.c