From c2c88e1f82e3d5981aff57873b08ff8b1d1e5cc6 Mon Sep 17 00:00:00 2001 From: Petr Shumilov Date: Tue, 19 May 2026 19:43:34 +0300 Subject: [PATCH 1/4] Use alloc_align for coroutine frame allocation Signed-off-by: Petr Shumilov --- compiler/compiler-settings.cpp | 5 +++++ runtime-light/coroutine/task.h | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/compiler-settings.cpp b/compiler/compiler-settings.cpp index d096a120ac..2afde09710 100644 --- a/compiler/compiler-settings.cpp +++ b/compiler/compiler-settings.cpp @@ -340,6 +340,11 @@ void CompilerSettings::init() { if (!dynamic_incremental_linkage.get()) { ss << " -fvisibility=hidden"; } + if (vk::contains(cxx.get(), "clang")) { + // To avoid undefined behavior, the coroutine frame must be allocated using the aligned operator new overload (the one that takes a std::align_val_t) + // Details: https://github.com/llvm/llvm-project/commit/327141fb1d8ca35b323107a43d57886eb77e7384 + ss << " -fcoro-aligned-allocation"; + } // Temporary solution. Required for allocator functions replacement in timelib ss << " -DTIMELIB_ALLOC_FUNC_PREFIX=timelib_"; } else { diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index 7ba3fb2041..c64fe8c13e 100644 --- a/runtime-light/coroutine/task.h +++ b/runtime-light/coroutine/task.h @@ -69,7 +69,12 @@ struct promise_base : kphp::coro::async_stack_element { return kphp::memory::script::alloc(n); } - auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + + auto operator delete([[maybe_unused]] void* ptr, [[maybe_unused]] size_t n) noexcept -> void { kphp::memory::script::free(ptr); } From 34c31f395424426486c57f83f68405279187fe10 Mon Sep 17 00:00:00 2001 From: Petr Shumilov Date: Wed, 20 May 2026 15:09:41 +0300 Subject: [PATCH 2/4] Add test Signed-off-by: Petr Shumilov --- .../phpt/fork/043_sched_yield_in_callback.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/phpt/fork/043_sched_yield_in_callback.php diff --git a/tests/phpt/fork/043_sched_yield_in_callback.php b/tests/phpt/fork/043_sched_yield_in_callback.php new file mode 100644 index 0000000000..7a7f847bbf --- /dev/null +++ b/tests/phpt/fork/043_sched_yield_in_callback.php @@ -0,0 +1,21 @@ +@ok + Date: Wed, 20 May 2026 16:35:53 +0300 Subject: [PATCH 3/4] Use alloc_align for all coroutine-related heap-allocated types Signed-off-by: Petr Shumilov --- runtime-light/coroutine/detail/await-set.h | 10 ++++++++++ runtime-light/coroutine/detail/task-self-deleting.h | 5 +++++ runtime-light/coroutine/detail/when-all.h | 5 +++++ runtime-light/coroutine/detail/when-any.h | 5 +++++ runtime-light/coroutine/shared-task.h | 5 +++++ runtime-light/coroutine/task.h | 2 +- 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 2edbd086e4..2c05be0c6e 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -61,6 +61,11 @@ class await_broker { return kphp::memory::script::alloc(n); } + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { kphp::memory::script::free(ptr); } @@ -168,6 +173,11 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element { return kphp::memory::script::alloc(n); } + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/task-self-deleting.h b/runtime-light/coroutine/detail/task-self-deleting.h index daa72cdd17..3ff19f1831 100644 --- a/runtime-light/coroutine/detail/task-self-deleting.h +++ b/runtime-light/coroutine/detail/task-self-deleting.h @@ -35,6 +35,11 @@ struct promise_self_deleting : kphp::coro::async_stack_element { return kphp::memory::script::alloc(n); } + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-all.h b/runtime-light/coroutine/detail/when-all.h index cc276f7996..2386e4fbb6 100644 --- a/runtime-light/coroutine/detail/when-all.h +++ b/runtime-light/coroutine/detail/when-all.h @@ -155,6 +155,11 @@ class when_all_task_promise_base : public kphp::coro::async_stack_element { return kphp::memory::script::alloc(n); } + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-any.h b/runtime-light/coroutine/detail/when-any.h index 2d7df28e37..f5ee01549e 100644 --- a/runtime-light/coroutine/detail/when-any.h +++ b/runtime-light/coroutine/detail/when-any.h @@ -165,6 +165,11 @@ class when_any_task_promise_base : public kphp::coro::async_stack_element { return kphp::memory::script::alloc(n); } + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/shared-task.h b/runtime-light/coroutine/shared-task.h index c0a3c1cc29..ddec603c58 100644 --- a/runtime-light/coroutine/shared-task.h +++ b/runtime-light/coroutine/shared-task.h @@ -148,6 +148,11 @@ struct promise_base : kphp::coro::async_stack_element { return kphp::memory::script::alloc(n); } + template + auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + return kphp::memory::script::alloc_aligned(n, al); + } + auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index c64fe8c13e..d5c064720b 100644 --- a/runtime-light/coroutine/task.h +++ b/runtime-light/coroutine/task.h @@ -74,7 +74,7 @@ struct promise_base : kphp::coro::async_stack_element { return kphp::memory::script::alloc_aligned(n, al); } - auto operator delete([[maybe_unused]] void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { kphp::memory::script::free(ptr); } From c91f0dddaeccd1c779a03fe172e0a55046f406bd Mon Sep 17 00:00:00 2001 From: Petr Shumilov Date: Wed, 20 May 2026 17:35:31 +0300 Subject: [PATCH 4/4] Add -fcoro-aligned-allocation into cmake Signed-off-by: Petr Shumilov --- runtime-light/runtime-light.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/runtime-light.cmake b/runtime-light/runtime-light.cmake index 3b5494afd4..e5df2f323d 100644 --- a/runtime-light/runtime-light.cmake +++ b/runtime-light/runtime-light.cmake @@ -2,7 +2,7 @@ include(${THIRD_PARTY_DIR}/pcre2-cmake/pcre2.cmake) # ================================================================================================= -set(RUNTIME_LIGHT_COMPILE_FLAGS -stdlib=libc++ ${RUNTIME_LIGHT_VISIBILITY}) +set(RUNTIME_LIGHT_COMPILE_FLAGS -stdlib=libc++ -fcoro-aligned-allocation ${RUNTIME_LIGHT_VISIBILITY}) set(RUNTIME_LIGHT_PLATFORM_SPECIFIC_LINK_FLAGS) if(APPLE)