-
Notifications
You must be signed in to change notification settings - Fork 224
Support for free-threaded build of CPython #504
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
Closed
Closed
Changes from 8 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
cabb466
Use strong reference APIs.
nascheme 6f5f3b6
Add work-around to crash in ~object_base().
nascheme cfbefe8
Use re-entrant mutex to protect global state.
nascheme fc68878
Set the Py_MOD_GIL_NOT_USED flag on modules.
nascheme 80731af
Add "nogil" option for BOOST_PYTHON_MODULE_INIT.
nascheme b7b0137
Update Linux CI scripts, more Python versions.
nascheme 597df1d
Add test-ubuntu-py-ver.yml workflow.
nascheme b44fcf1
Simplify CI workflow.
nascheme 6a9c341
Fix typo, should be "clang++".
nascheme 93c5612
Add Python 2.x workflow.
nascheme c6d5d5a
Improve Python 2.x compatibility.
nascheme File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #!/usr/bin/env python3 | ||
| # | ||
| # Determine info about the Python install and write shell code to stdout, to | ||
| # set env variables. This will set the variables PY_LDFLAGS, PY_CFLAGS and | ||
| # PY_INC_PATH. | ||
| # | ||
| # The python3-config tool is used as the source of this info. In theory we | ||
| # could use sysconfig as well but the setup-python action from github appears | ||
| # to patch python3-config but not patch the sysconfig info. | ||
| # | ||
| # Usage: | ||
| # eval $(python3 get-py-env.py) | ||
|
|
||
| import os | ||
| import re | ||
| import subprocess | ||
|
|
||
|
|
||
| def get_output(cmd): | ||
| rv = subprocess.run( | ||
| cmd, | ||
| capture_output=True, # Capture stdout and stderr | ||
| text=True, # Decode output as text (UTF-8) | ||
| check=True, # Raise an error if the command fails | ||
| ) | ||
| return rv.stdout | ||
|
|
||
|
|
||
| def extract_flags(cmd, prefix): | ||
| flags = [] | ||
| for part in get_output(cmd).split(): | ||
| part = part.strip() | ||
| if part.startswith(prefix): | ||
| flags.append(part) | ||
| return ' '.join(flags) | ||
|
|
||
|
|
||
| def find_python_h(): | ||
| """Find the include path that has Python.h contained inside. | ||
| We could use INCLUDEPY from sysconfig but github patches | ||
| python3-config but not the sysconfig info (after moving the | ||
| install). | ||
| """ | ||
| c_flags = extract_flags(['python3-config', '--cflags'], '-I') | ||
| for part in c_flags.split(): | ||
| m = re.search(r'-I(\S+)', part) | ||
| if not m: | ||
| continue | ||
| inc_path = m.group(1) | ||
| if os.path.exists(os.path.join(inc_path, 'Python.h')): | ||
| return inc_path | ||
| raise SystemExit('cannot find Python.h') | ||
|
|
||
|
|
||
| def main(): | ||
| ld_flags = extract_flags(['python3-config', '--ldflags'], '-L') | ||
| c_flags = extract_flags(['python3-config', '--cflags'], '-I') | ||
| include_path = find_python_h() | ||
| print(f'PY_LDFLAGS="{ld_flags}"') | ||
| print(f'PY_CFLAGS="{c_flags}"') | ||
| print(f'PY_INC_PATH="{include_path}"') | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| #!/bin/sh | ||
|
|
||
| set -eu | ||
|
|
||
| echo "cxx version: $CXX $($CXX --version)" | ||
| echo "cxx std: $CXX_STD" | ||
| echo "python3 path: $(which python3)" | ||
| echo "python3 version: $(python3 --version)" | ||
|
|
||
| if ! which faber > /dev/null; then | ||
| echo "Installing faber..." | ||
| python3 -m pip install --upgrade pip | ||
| python3 -m pip install -U faber | ||
| fi | ||
| echo "faber version: $(faber -v)" | ||
|
|
||
| # find and set PY_LDFLAGS and PY_INC_PATH | ||
| eval $(python3 .github/get-py-env.py) | ||
|
|
||
| echo "PY_INC_PATH=$PY_INC_PATH" | ||
| echo "PY_LDFLAGS=$PY_LDFLAGS" | ||
|
|
||
| case $(python3-config --abiflags) in | ||
| *t*) | ||
| # When running with free-threaded, we always want to disable the GIL | ||
| # even for extensions without the mod_gil_not_used() flag | ||
| export PYTHON_GIL=0 | ||
| ;; | ||
| esac | ||
|
|
||
| # this could be set by LD_LIBRARY_PATH but faber overrides it | ||
| prefix=$(python3-config --prefix) | ||
| echo "${prefix}/lib" > /etc/ld.so.conf.d/boost-ci.conf && ldconfig | ||
|
|
||
| sed -e "s/\$PYTHON/python3/g" .ci/faber > $HOME/.faber | ||
|
|
||
| faber \ | ||
| --with-boost-include=${BOOST_PY_DEPS} \ | ||
| --builddir=build \ | ||
| cxx.name="${CXX}" \ | ||
| cxxflags="-std=${CXX_STD}" \ | ||
| cppflags="-std=${CXX_STD}" \ | ||
| include="${PY_INC_PATH}" \ | ||
| ldflags="${PY_LDFLAGS}" \ | ||
| -j`nproc` \ | ||
| "$@" |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| // Copyright 2025 Boost.Python Contributors | ||
| // Distributed under the Boost Software License, Version 1.0. (See | ||
| // accompanying file LICENSE_1_0.txt or copy at | ||
| // http://www.boost.org/LICENSE_1_0.txt) | ||
|
|
||
| #ifndef BOOST_PYTHON_DETAIL_PYMUTEX_HPP | ||
| #define BOOST_PYTHON_DETAIL_PYMUTEX_HPP | ||
|
|
||
| #include <boost/python/detail/prefix.hpp> | ||
| #ifdef Py_GIL_DISABLED | ||
| // needed for pymutex wrapper | ||
| #include <atomic> | ||
| #include <cstddef> | ||
| #endif | ||
|
|
||
| namespace boost { namespace python { namespace detail { | ||
|
|
||
| #ifdef Py_GIL_DISABLED | ||
|
|
||
| // Re-entrant wrapper around PyMutex for free-threaded Python | ||
| // Similar to _PyRecursiveMutex or threading.RLock | ||
| class pymutex { | ||
| PyMutex m_mutex; | ||
| std::atomic<unsigned long> m_owner; | ||
| std::size_t m_level; | ||
|
|
||
| public: | ||
| pymutex() : m_mutex({}), m_owner(0), m_level(0) {} | ||
|
|
||
| // Non-copyable, non-movable | ||
| pymutex(const pymutex&) = delete; | ||
| pymutex& operator=(const pymutex&) = delete; | ||
|
|
||
| void lock() { | ||
| unsigned long thread = PyThread_get_thread_ident(); | ||
| if (m_owner.load(std::memory_order_relaxed) == thread) { | ||
| m_level++; | ||
| return; | ||
| } | ||
| PyMutex_Lock(&m_mutex); | ||
| m_owner.store(thread, std::memory_order_relaxed); | ||
| // m_level should be 0 when we acquire the lock | ||
| } | ||
|
|
||
| void unlock() { | ||
| unsigned long thread = PyThread_get_thread_ident(); | ||
| // Verify current thread owns the lock | ||
| if (m_owner.load(std::memory_order_relaxed) != thread) { | ||
| // This should never happen - programming error | ||
| return; | ||
| } | ||
| if (m_level > 0) { | ||
| m_level--; | ||
| return; | ||
| } | ||
| m_owner.store(0, std::memory_order_relaxed); | ||
| PyMutex_Unlock(&m_mutex); | ||
| } | ||
|
|
||
| bool is_locked_by_current_thread() const { | ||
| unsigned long thread = PyThread_get_thread_ident(); | ||
| return m_owner.load(std::memory_order_relaxed) == thread; | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| // RAII lock guard for pymutex | ||
| class pymutex_guard { | ||
| pymutex& m_mutex; | ||
|
|
||
| public: | ||
| explicit pymutex_guard(pymutex& mutex) : m_mutex(mutex) { | ||
| m_mutex.lock(); | ||
| } | ||
|
|
||
| ~pymutex_guard() { | ||
| m_mutex.unlock(); | ||
| } | ||
|
|
||
| // Non-copyable, non-movable | ||
| pymutex_guard(const pymutex_guard&) = delete; | ||
| pymutex_guard& operator=(const pymutex_guard&) = delete; | ||
| }; | ||
|
|
||
| // Global mutex for protecting all Boost.Python internal state | ||
| // Similar to pybind11's internals.mutex | ||
| BOOST_PYTHON_DECL pymutex& get_global_mutex(); | ||
|
|
||
| // Macro for acquiring the global lock | ||
| // Similar to pybind11's PYBIND11_LOCK_INTERNALS | ||
| #define BOOST_PYTHON_LOCK_STATE() \ | ||
| ::boost::python::detail::pymutex_guard lock(::boost::python::detail::get_global_mutex()) | ||
|
|
||
| #else | ||
|
|
||
| // No-op macro when not in free-threaded mode | ||
| #define BOOST_PYTHON_LOCK_STATE() | ||
|
|
||
| #endif // Py_GIL_DISABLED | ||
|
|
||
| }}} // namespace boost::python::detail | ||
|
|
||
| #endif // BOOST_PYTHON_DETAIL_PYMUTEX_HPP |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.
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.
that looks like a typo. The value should probably be
clang++.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.
Yup, I change that line from g++ to clang++ at the last moment (thinking maybe Clang will be a bit faster for CI runs). Fixed now but a lesson to not change things at the last minute, after you tested them.