Skip to content
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

Builtin zip method is not safe under free-threading #123271

Closed
eendebakpt opened this issue Aug 23, 2024 · 1 comment
Closed

Builtin zip method is not safe under free-threading #123271

eendebakpt opened this issue Aug 23, 2024 · 1 comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-free-threading type-bug An unexpected behavior, bug, or error

Comments

@eendebakpt
Copy link
Contributor

eendebakpt commented Aug 23, 2024

Bug report

Bug description:

A common optimization technique (used in zip_next) for methods generating tuples is to keep an internal reference to the returned tuple and when the method is called again check whether the internal tuple has reference count 1. If the refcount is one, the tuple can be re-used. Under the free-threading build this is not safe: after the check on the reference count another thread can perform the same check and also re-use the tuple. This can lead to a double decref on the items of the tuple replaced and a double incref (memory leak) on the items of the tuple being set.

Some options to address this:

  • Stop the internal tracking of the tuple. This affects performance of zip when the tuple can be re-used
  • Lock the entire zip object during a call to zip_next. This is expensive, also for the single-threaded case.
  • Use an atomic "check refcount equals one and conditionally increment" method. This could be less expensive than locking the entire object, but maybe this method is not available or almost as expensive as locking the object.

A minimal example reproducing the issue is added as a test in #123272. It shows the issue when executed with a free-threading debug build.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Windows

Linked PRs

@eendebakpt eendebakpt added the type-bug An unexpected behavior, bug, or error label Aug 23, 2024
@picnixz
Copy link
Contributor

picnixz commented Aug 23, 2024

(Wrong zip, my bad)

@picnixz picnixz added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Aug 23, 2024
colesbury pushed a commit that referenced this issue Aug 27, 2024
The `zip_next` function uses a common optimization technique for methods
that generate tuples. The iterator maintains an internal reference to
the returned tuple. When the method is called again, it checks if the
internal tuple's reference count is 1. If so, the tuple can be reused.
However, this approach is not safe under the free-threading build:
after checking the reference count, another thread may perform the same
check and also reuse the tuple. This can result in a double decref on
the items of the replaced tuple and a double incref (memory leak) on
the items of the tuple being set.

This adds a function, `_PyObject_IsUniquelyReferenced` that
encapsulates the stricter logic necessary for the free-threaded build:
the internal tuple must be owned by the current thread, have a local
refcount of one, and a shared refcount of zero.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-free-threading type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants