Skip to content
Merged
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
38 changes: 21 additions & 17 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,11 @@ jobs:
- arm64
free-threading:
- false
- true
exclude:
# Skip Win32 on free-threaded builds
- { arch: Win32, free-threading: true }
# TODO(Immutable): Enable free-threading build when it is made to work.
# - true
# exclude:
# # Skip Win32 on free-threaded builds
# - { arch: Win32, free-threading: true }
uses: ./.github/workflows/reusable-windows.yml
with:
arch: ${{ matrix.arch }}
Expand Down Expand Up @@ -209,10 +210,11 @@ jobs:
- macos-15-intel
free-threading:
- false
- true
exclude:
- os: macos-15-intel
free-threading: true
# TODO(Immutable): Enable free-threading build when it is made to work.
# - true
# exclude:
# - os: macos-15-intel
# free-threading: true
uses: ./.github/workflows/reusable-macos.yml
with:
config_hash: ${{ needs.build-context.outputs.config-hash }}
Expand All @@ -234,14 +236,15 @@ jobs:
- true
free-threading:
- false
- true
# TODO(Immutable): Enable free-threading build when it is made to work.
# - true
os:
- ubuntu-24.04
- ubuntu-24.04-arm
exclude:
# Do not test BOLT with free-threading, to conserve resources
- bolt: true
free-threading: true
# # Do not test BOLT with free-threading, to conserve resources
# - bolt: true
# free-threading: true
# BOLT currently crashes during instrumentation on aarch64
- os: ubuntu-24.04-arm
bolt: true
Expand Down Expand Up @@ -614,13 +617,14 @@ jobs:
- Thread
free-threading:
- false
- true
# TODO(Immutable): Enable free-threading build when it is made to work.
# - true
sanitizer:
- TSan
include:
- check-name: Undefined behavior
sanitizer: UBSan
free-threading: false
# include:
# - check-name: Undefined behavior
# sanitizer: UBSan
# free-threading: false
uses: ./.github/workflows/reusable-san.yml
with:
sanitizer: ${{ matrix.sanitizer }}
Expand Down
54 changes: 27 additions & 27 deletions .github/workflows/jit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,33 +129,33 @@ jobs:
make all --jobs 4
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

jit-with-disabled-gil:
name: Free-Threaded (Debug)
needs: interpreter
runs-on: ubuntu-24.04
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
llvm:
- 19
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build with JIT enabled and GIL disabled
run: |
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
./configure --enable-experimental-jit --with-pydebug --disable-gil
make all --jobs 4
- name: Run tests
run: |
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
continue-on-error: true
# jit-with-disabled-gil:
# name: Free-Threaded (Debug)
# needs: interpreter
# runs-on: ubuntu-24.04
# timeout-minutes: 90
# strategy:
# fail-fast: false
# matrix:
# llvm:
# - 19
# steps:
# - uses: actions/checkout@v4
# with:
# persist-credentials: false
# - uses: actions/setup-python@v5
# with:
# python-version: '3.11'
# - name: Build with JIT enabled and GIL disabled
# run: |
# sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
# export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
# ./configure --enable-experimental-jit --with-pydebug --disable-gil
# make all --jobs 4
# - name: Run tests
# run: |
# ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
# continue-on-error: true

no-opt-jit:
name: JIT without optimizations (Debug)
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/tail-call.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ jobs:
make all --jobs 4
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

- name: Native Linux with free-threading (release)
if: matrix.target == 'free-threading'
run: |
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
CC=clang-20 ./configure --with-tail-call-interp --disable-gil
make all --jobs 4
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
# - name: Native Linux with free-threading (release)
# if: matrix.target == 'free-threading'
# run: |
# sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
# export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
# CC=clang-20 ./configure --with-tail-call-interp --disable-gil
# make all --jobs 4
# ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
48 changes: 26 additions & 22 deletions Include/internal/pycore_freelist_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,33 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

#define MAXFREELIST(x) x
//#define MAXFREELIST(x) 0
Comment on lines +11 to +12
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be part of the change set or is this an artifact of development/Debugging?

If this should be kept, it should be used for all _MAXFREELIST values in this file

Copy link
Owner Author

Choose a reason for hiding this comment

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

I found this useful for debugging memory safety issues with Asan. The freelists hide UAF and double-free that Asan would detect.
I think this is something that we could try to push into CPython for the sanitizer builds.

I am happy to drop from this PR. It isn't essential. I just would sooner not lose the code.



# define PyTuple_MAXSAVESIZE 20 // Largest tuple to save on freelist
# define Py_tuple_MAXFREELIST 2000 // Maximum number of tuples of each size to save
# define Py_lists_MAXFREELIST 80
# define Py_list_iters_MAXFREELIST 10
# define Py_tuple_iters_MAXFREELIST 10
# define Py_dicts_MAXFREELIST 80
# define Py_dictkeys_MAXFREELIST 80
# define Py_floats_MAXFREELIST 100
# define Py_complexes_MAXFREELIST 100
# define Py_ints_MAXFREELIST 100
# define Py_slices_MAXFREELIST 1
# define Py_ranges_MAXFREELIST 6
# define Py_range_iters_MAXFREELIST 6
# define Py_contexts_MAXFREELIST 255
# define Py_async_gens_MAXFREELIST 80
# define Py_async_gen_asends_MAXFREELIST 80
# define Py_futureiters_MAXFREELIST 255
# define Py_object_stack_chunks_MAXFREELIST 4
# define Py_unicode_writers_MAXFREELIST 1
# define Py_bytes_writers_MAXFREELIST 1
# define Py_pycfunctionobject_MAXFREELIST 16
# define Py_pycmethodobject_MAXFREELIST 16
# define Py_pymethodobjects_MAXFREELIST 20
# define Py_tuple_MAXFREELIST MAXFREELIST(2000) // Maximum number of tuples of each size to save
# define Py_lists_MAXFREELIST MAXFREELIST(80)
# define Py_list_iters_MAXFREELIST MAXFREELIST(10)
# define Py_tuple_iters_MAXFREELIST MAXFREELIST(10)
# define Py_dicts_MAXFREELIST MAXFREELIST(80)
# define Py_dictkeys_MAXFREELIST MAXFREELIST(80)
# define Py_floats_MAXFREELIST MAXFREELIST(100)
# define Py_complexes_MAXFREELIST MAXFREELIST(100)
# define Py_ints_MAXFREELIST MAXFREELIST(100)
# define Py_slices_MAXFREELIST MAXFREELIST(1)
# define Py_ranges_MAXFREELIST MAXFREELIST(6)
# define Py_range_iters_MAXFREELIST MAXFREELIST(6)
# define Py_contexts_MAXFREELIST MAXFREELIST(255)
# define Py_async_gens_MAXFREELIST MAXFREELIST(80)
# define Py_async_gen_asends_MAXFREELIST MAXFREELIST(80)
# define Py_futureiters_MAXFREELIST MAXFREELIST(255)
# define Py_object_stack_chunks_MAXFREELIST MAXFREELIST(4)
# define Py_unicode_writers_MAXFREELIST MAXFREELIST(1)
# define Py_bytes_writers_MAXFREELIST MAXFREELIST(1)
# define Py_pycfunctionobject_MAXFREELIST MAXFREELIST(16)
# define Py_pycmethodobject_MAXFREELIST MAXFREELIST(16)
# define Py_pymethodobjects_MAXFREELIST MAXFREELIST(20)

// A generic freelist of either PyObjects or other data structures.
struct _Py_freelist {
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__floor__)
STRUCT_FOR_ID(__floordiv__)
STRUCT_FOR_ID(__format__)
STRUCT_FOR_ID(__freezable__)
STRUCT_FOR_ID(__fspath__)
STRUCT_FOR_ID(__ge__)
STRUCT_FOR_ID(__get__)
Expand Down Expand Up @@ -186,6 +187,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__path__)
STRUCT_FOR_ID(__pos__)
STRUCT_FOR_ID(__pow__)
STRUCT_FOR_ID(__pre_freeze__)
STRUCT_FOR_ID(__prepare__)
STRUCT_FOR_ID(__qualname__)
STRUCT_FOR_ID(__radd__)
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ struct _is {

// Dictionary of the sys module
PyObject *sysdict;
PyObject *mutable_modules;

// Dictionary of the builtins module
PyObject *builtins;
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ _Py_memory_repeat(char* dest, Py_ssize_t len_dest, Py_ssize_t len_src)
}
}

PyAPI_FUNC(PyObject*) _Py_ListPop(PyListObject *self, Py_ssize_t index);

typedef struct {
PyObject_HEAD
Py_ssize_t it_index;
Expand Down
32 changes: 27 additions & 5 deletions Include/internal/pycore_moduleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,50 @@ extern int _PyModule_IsExtension(PyObject *obj);

typedef struct {
PyObject_HEAD
// For immutable modules to find the mutable state and
// for logging purposes after md_dict is cleared
PyObject *md_name;
int md_frozen;
Copy link
Collaborator

Choose a reason for hiding this comment

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

FIXME: @xFrednet, Investigate if this field can be avoided with the change of the Module object type as part of the pre-freeze hook.


// *******************************************************
// Module state, only available on mutable module objects
// *******************************************************
PyObject *md_dict;
PyModuleDef *md_def;
void *md_state;
PyObject *md_weaklist;
// for logging purposes after md_dict is cleared
PyObject *md_name;
#ifdef Py_GIL_DISABLED
void *md_gil;
#endif
} PyModuleObject;

PyAPI_FUNC(PyModuleObject*) _PyInterpreterState_GetModuleState(PyObject *mod);

static inline PyModuleDef* _PyModule_GetDef(PyObject *mod) {
assert(PyModule_Check(mod));
return ((PyModuleObject *)mod)->md_def;
PyModuleObject *state = _PyInterpreterState_GetModuleState(mod);
if (state == NULL) {
return NULL;
}
return state->md_def;
}

static inline void* _PyModule_GetState(PyObject* mod) {
assert(PyModule_Check(mod));
return ((PyModuleObject *)mod)->md_state;
PyModuleObject *state = _PyInterpreterState_GetModuleState(mod);
if (state == NULL) {
return NULL;
}
return state->md_state;
}

static inline PyObject* _PyModule_GetDict(PyObject *mod) {
assert(PyModule_Check(mod));
PyObject *dict = ((PyModuleObject *)mod) -> md_dict;
PyModuleObject *state = _PyInterpreterState_GetModuleState(mod);
if (state == NULL) {
return NULL;
}
PyObject *dict = state -> md_dict;
// _PyModule_GetDict(mod) must not be used after calling module_clear(mod)
assert(dict != NULL);
return dict; // borrowed reference
Expand All @@ -56,6 +76,8 @@ extern Py_ssize_t _PyModule_GetFilenameUTF8(
PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress);
PyObject* _Py_module_getattro(PyObject *m, PyObject *name);

extern int _Py_module_freeze_hook(PyObject *m);

#ifdef __cplusplus
}
#endif
Expand Down
Loading
Loading