diff --git a/.github/workflows/unit-test-cpp.yml b/.github/workflows/unit-test-cpp.yml index 7a0605c8b..3e46d6920 100644 --- a/.github/workflows/unit-test-cpp.yml +++ b/.github/workflows/unit-test-cpp.yml @@ -80,6 +80,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v7 + - name: Set up Python for clang-format + if: runner.os == 'Linux' + uses: actions/setup-python@v7 + with: + python-version: "3.11" + - name: Set up JDK 17 uses: actions/setup-java@v6.0.1 with: @@ -121,10 +127,10 @@ jobs: elif command -v yum >/dev/null 2>&1; then sudo yum install -y libuuid-devel fi - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 100 - sudo update-alternatives --set clang-format /usr/bin/clang-format-17 - sudo apt-get update - sudo apt-get install -y uuid-dev + python -m venv "$RUNNER_TEMP/clang-format" + "$RUNNER_TEMP/clang-format/bin/python" -m pip install --only-binary=:all: clang-format==17.0.6 + "$RUNNER_TEMP/clang-format/bin/clang-format" --version + echo "$RUNNER_TEMP/clang-format/bin" >> "$GITHUB_PATH" elif [[ "$RUNNER_OS" == "Windows" ]]; then choco install llvm --version 17.0.6 --force else diff --git a/.github/workflows/unit-test-python.yml b/.github/workflows/unit-test-python.yml index b01c09341..854774034 100644 --- a/.github/workflows/unit-test-python.yml +++ b/.github/workflows/unit-test-python.yml @@ -45,6 +45,13 @@ jobs: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] python-version: ["3.9", "3.11", "3.14"] + exclude: + - os: ubuntu-latest + python-version: "3.9" + include: + # Python 3.9 has no setup-python build for Ubuntu 26.04. + - os: ubuntu-22.04 + python-version: "3.9" runs-on: ${{ matrix.os }} steps: @@ -92,8 +99,10 @@ jobs: shell: bash run: | if [[ "$RUNNER_OS" == "Linux" ]]; then - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 100 - sudo update-alternatives --set clang-format /usr/bin/clang-format-17 + python -m venv "$RUNNER_TEMP/clang-format" + "$RUNNER_TEMP/clang-format/bin/python" -m pip install --only-binary=:all: clang-format==17.0.6 + "$RUNNER_TEMP/clang-format/bin/clang-format" --version + echo "$RUNNER_TEMP/clang-format/bin" >> "$GITHUB_PATH" sudo apt-get update sudo apt-get install -y uuid-dev elif [[ "$RUNNER_OS" == "Windows" ]]; then diff --git a/cpp/src/common/allocator/page_arena.cc b/cpp/src/common/allocator/page_arena.cc index 8abfd9d0e..47d8be421 100644 --- a/cpp/src/common/allocator/page_arena.cc +++ b/cpp/src/common/allocator/page_arena.cc @@ -45,7 +45,12 @@ char* PageArena::alloc(uint32_t size) { return new_page->alloc(size); } else { void* ptr = base_allocator_.alloc(size + sizeof(Page), mid_); - Page* new_page = new (ptr) Page(dummy_head_.next_); + if (UNLIKELY(ptr == nullptr)) { + return nullptr; + } + Page* new_page = new (ptr) Page(size, dummy_head_.next_); + // A dedicated oversized page is fully consumed and cannot be reused. + new_page->cur_alloc_ = new_page->page_end_; dummy_head_.next_ = new_page; return reinterpret_cast(new_page) + sizeof(Page); } diff --git a/cpp/src/common/allocator/page_arena.h b/cpp/src/common/allocator/page_arena.h index c0dfbebb9..40aad4c96 100644 --- a/cpp/src/common/allocator/page_arena.h +++ b/cpp/src/common/allocator/page_arena.h @@ -76,8 +76,6 @@ class PageArena { class Page { public: Page() : next_(nullptr), page_end_(nullptr), cur_alloc_(nullptr) {} - explicit Page(Page* next_page) - : next_(next_page), page_end_(nullptr), cur_alloc_(nullptr) {} Page(uint32_t page_size, Page* next_page) { next_ = next_page; cur_alloc_ = diff --git a/cpp/test/common/allocator/page_arena_test.cc b/cpp/test/common/allocator/page_arena_test.cc index 0d34e95d8..e146685cf 100644 --- a/cpp/test/common/allocator/page_arena_test.cc +++ b/cpp/test/common/allocator/page_arena_test.cc @@ -55,6 +55,37 @@ TEST(PageArenaTest, AllocMoreThanPageSize) { page_arena.reset(); } +TEST(PageArenaTest, OversizedAllocationTracksUsedBytesAndPreservesData) { + PageArena arena; + arena.init(64, MOD_DEFAULT); + char* large = arena.alloc(256); + ASSERT_NE(large, nullptr); + memset(large, 'x', 256); + ASSERT_EQ(arena.get_total_used_bytes(), 256); + + char* small = arena.alloc(8); + ASSERT_NE(small, nullptr); + memset(small, 'y', 8); + EXPECT_EQ(arena.get_total_used_bytes(), 264); + EXPECT_EQ(arena.TEST_get_page_count(), 2); + for (int i = 0; i < 256; i++) { + ASSERT_EQ(large[i], 'x'); + } + arena.reset(); + EXPECT_EQ(arena.get_total_used_bytes(), 0); +} + +TEST(PageArenaTest, ConsecutiveOversizedAllocationsTrackUsedBytes) { + PageArena arena; + arena.init(64, MOD_DEFAULT); + ASSERT_NE(arena.alloc(256), nullptr); + ASSERT_NE(arena.alloc(512), nullptr); + ASSERT_EQ(arena.get_total_used_bytes(), 768); + EXPECT_EQ(arena.TEST_get_page_count(), 2); + ASSERT_NE(arena.alloc(8), nullptr); + EXPECT_EQ(arena.get_total_used_bytes(), 776); +} + struct MyStruct { double x; int64_t y;