diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 13bb270a3b..ea72052b66 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -4,6 +4,7 @@
+
diff --git a/builtin-functions/kphp-light/array.txt b/builtin-functions/kphp-light/array.txt
index c761056875..f88badd317 100644
--- a/builtin-functions/kphp-light/array.txt
+++ b/builtin-functions/kphp-light/array.txt
@@ -107,3 +107,14 @@ function rsort (&$a ::: array, $flag ::: int = SORT_REGULAR) ::: void;
function sort (&$a ::: array, $flag ::: int = SORT_REGULAR) ::: void;
+/** @kphp-extern-func-info interruptible */
+function uksort (&$a ::: array, callable(mixed $x, mixed $y):int $callback) ::: void;
+
+/** @kphp-extern-func-info interruptible */
+function usort (&$a ::: array, callable(^1[*] $x, ^1[*] $y):int $callback) ::: void;
+
+/** @kphp-extern-func-info interruptible */
+function uasort (&$a ::: array, callable(^1[*] $x, ^1[*] $y):int $callback) ::: void;
+
+
+
diff --git a/builtin-functions/kphp-light/unsupported/arrays.txt b/builtin-functions/kphp-light/unsupported/arrays.txt
index a1fdee0cb5..6d8a78de70 100644
--- a/builtin-functions/kphp-light/unsupported/arrays.txt
+++ b/builtin-functions/kphp-light/unsupported/arrays.txt
@@ -46,10 +46,6 @@ function array_is_vector ($a ::: array) ::: bool;
function array_is_list ($a ::: array) ::: bool;
-function uasort (&$a ::: array, callable(^1[*] $x, ^1[*] $y):int $callback) ::: void;
-function uksort (&$a ::: array, callable(mixed $x, mixed $y):int $callback) ::: void;
-function usort (&$a ::: array, callable(^1[*] $x, ^1[*] $y):int $callback) ::: void;
-
/** @kphp-extern-func-info cpp_template_call */
function vk_dot_product ($a ::: array, $b ::: array) ::: ^1[*] | ^2[*];
diff --git a/compiler/code-gen/declarations.cpp b/compiler/code-gen/declarations.cpp
index e1218376c5..be6c236fbc 100644
--- a/compiler/code-gen/declarations.cpp
+++ b/compiler/code-gen/declarations.cpp
@@ -115,8 +115,8 @@ void FunctionParams::declare_cpp_param(CodeGenerator &W, VertexAdaptor v
auto var_ptr = var->var_id;
if (var->ref_flag) {
W << "&";
- } else if (!function->is_k2_fork && (var_ptr->marked_as_const || (!function->has_variadic_param && var_ptr->is_read_only))) {
- // the top of k2 fork must take arguments by value (see C++ avoid reference parameters in coroutines)
+ } else if (!function->is_interruptible && (var_ptr->marked_as_const || (!function->has_variadic_param && var_ptr->is_read_only))) {
+ // interruptible function must take arguments by value (see C++ avoid reference parameters in coroutines)
W << (!type.type->is_primitive_type() ? "const &" : "");
}
W << VarName(var_ptr);
diff --git a/runtime-common/core/core-types/decl/array_decl.inl b/runtime-common/core/core-types/decl/array_decl.inl
index 893616334e..fcf7cdc0a3 100644
--- a/runtime-common/core/core-types/decl/array_decl.inl
+++ b/runtime-common/core/core-types/decl/array_decl.inl
@@ -29,7 +29,18 @@ struct array_size {
namespace dl {
template
-void sort(TT *begin_init, TT *end_init, const T1 &compare);
+void sort(TT *begin_init, TT *end_init, T1 compare) noexcept;
+}
+
+namespace array_functions_impl_ {
+/*
+ * async analog of array::sort and array::ksort since in runtime-light comparator can be coroutine
+ * */
+template
+Result async_sort(array & arr, Comparator comparator, bool renumber) noexcept;
+
+template
+Result async_ksort(array & arr, Comparator comparator) noexcept;
}
enum class overwrite_element { YES, NO };
@@ -438,6 +449,12 @@ private:
template
friend class array;
+
+ template
+ friend Result array_functions_impl_::async_sort(array & arr, Comparator comparator, bool renumber) noexcept;
+
+ template
+ friend Result array_functions_impl_::async_ksort(array & arr, Comparator comparator) noexcept;
};
template
diff --git a/runtime-light/stdlib/array/array-functions.h b/runtime-light/stdlib/array/array-functions.h
index cde085d5a7..c52e6508b2 100644
--- a/runtime-light/stdlib/array/array-functions.h
+++ b/runtime-light/stdlib/array/array-functions.h
@@ -5,15 +5,198 @@
#pragma once
#include
+#include
#include
#include
#include "runtime-common/core/runtime-core.h"
+#include "runtime-common/stdlib/array/array-functions.h"
#include "runtime-light/coroutine/task.h"
#include "runtime-light/stdlib/math/random-functions.h"
#include "runtime-light/utils/concepts.h"
namespace array_functions_impl_ {
+template
+requires(std::invocable &&is_async_function_v) task_t async_sort(T *begin_init, T *end_init,
+ Comparator compare) noexcept {
+ T *begin_stack[32];
+ T *end_stack[32];
+
+ begin_stack[0] = begin_init;
+ end_stack[0] = end_init - 1;
+
+ for (int depth = 0; depth >= 0; --depth) {
+ T *begin = begin_stack[depth];
+ T *end = end_stack[depth];
+
+ while (begin < end) {
+ const auto offset = (end - begin) >> 1;
+ swap(*begin, begin[offset]);
+
+ T *i = begin + 1;
+ T *j = end;
+
+ while (true) {
+ while (i < j && (co_await std::invoke(compare, *begin, *i)) > 0) {
+ i++;
+ }
+
+ while (i <= j && (co_await std::invoke(compare, *j, *begin)) > 0) {
+ j--;
+ }
+
+ if (i >= j) {
+ break;
+ }
+
+ swap(*i++, *j--);
+ }
+
+ swap(*begin, *j);
+
+ if (j - begin <= end - j) {
+ if (j + 1 < end) {
+ begin_stack[depth] = j + 1;
+ end_stack[depth++] = end;
+ }
+ end = j - 1;
+ } else {
+ if (begin < j - 1) {
+ begin_stack[depth] = begin;
+ end_stack[depth++] = j - 1;
+ }
+ begin = j + 1;
+ }
+ }
+ }
+ co_return;
+}
+
+template
+Result async_sort(array &arr, Comparator comparator, bool renumber) noexcept {
+ using array_inner = typename array::array_inner;
+ using array_bucket = typename array::array_bucket;
+ int64_t n = arr.count();
+
+ if (renumber) {
+ if (n == 0) {
+ co_return;
+ }
+
+ if (!arr.is_vector()) {
+ array_inner *res = array_inner::create(n, true);
+ for (array_bucket *it = arr.p->begin(); it != arr.p->end(); it = arr.p->next(it)) {
+ res->push_back_vector_value(it->value);
+ }
+
+ arr.p->dispose();
+ arr.p = res;
+ } else {
+ arr.mutate_if_vector_shared();
+ }
+
+ U *begin = reinterpret_cast(arr.p->entries());
+ co_await async_sort(begin, begin + n, std::move(comparator));
+ co_return;
+ }
+
+ if (n <= 1) {
+ co_return;
+ }
+
+ if (arr.is_vector()) {
+ arr.convert_to_map();
+ } else {
+ arr.mutate_if_map_shared();
+ }
+ auto &runtimeAllocator{RuntimeAllocator::get()};
+
+ auto **arTmp = static_cast(runtimeAllocator.alloc_script_memory(n * sizeof(array_bucket *)));
+ uint32_t i = 0;
+ for (array_bucket *it = arr.p->begin(); it != arr.p->end(); it = arr.p->next(it)) {
+ arTmp[i++] = it;
+ }
+ php_assert(i == n);
+
+ const auto hash_entry_cmp = [](Compare compare, const array_bucket *lhs, const array_bucket *rhs) -> task_t {
+ co_return(co_await std::invoke(compare, lhs->value, rhs->value)) > 0;
+ };
+
+ const auto partial_hash_entry_cmp = std::bind_front(hash_entry_cmp, std::move(comparator));
+
+ co_await async_sort(arTmp, arTmp + n, partial_hash_entry_cmp);
+
+ arTmp[0]->prev = arr.p->get_pointer(arr.p->end());
+ arr.p->end()->next = arr.p->get_pointer(arTmp[0]);
+ for (uint32_t j = 1; j < n; j++) {
+ arTmp[j]->prev = arr.p->get_pointer(arTmp[j - 1]);
+ arTmp[j - 1]->next = arr.p->get_pointer(arTmp[j]);
+ }
+ arTmp[n - 1]->next = arr.p->get_pointer(arr.p->end());
+ arr.p->end()->prev = arr.p->get_pointer(arTmp[n - 1]);
+
+ runtimeAllocator.free_script_memory(arTmp, n * sizeof(array_bucket *));
+}
+
+template
+Result async_ksort(array &arr, Comparator comparator) noexcept {
+ using array_bucket = typename array::array_bucket;
+ using key_type = typename array::key_type;
+ using list_hash_entry = typename array::list_hash_entry;
+
+ int64_t n = arr.count();
+ if (n <= 1) {
+ co_return;
+ }
+
+ if (arr.is_vector()) {
+ arr.convert_to_map();
+ } else {
+ arr.mutate_if_map_shared();
+ }
+
+ array keys(array_size(n, true));
+ for (auto *it = arr.p->begin(); it != arr.p->end(); it = arr.p->next(it)) {
+ keys.p->push_back_vector_value(it->get_key());
+ }
+
+ auto *keysp = reinterpret_cast(keys.p->entries());
+ co_await async_sort(keysp, keysp + n, std::move(comparator));
+
+ auto *prev = static_cast(arr.p->end());
+ for (uint32_t j = 0; j < n; j++) {
+ list_hash_entry *cur = nullptr;
+ if (arr.is_int_key(keysp[j])) {
+ int64_t int_key = keysp[j].to_int();
+ uint32_t bucket = arr.p->choose_bucket(int_key);
+ while (arr.p->entries()[bucket].int_key != int_key || !arr.p->entries()[bucket].string_key.is_dummy_string()) {
+ if (++bucket == arr.p->buf_size) [[unlikely]] {
+ bucket = 0;
+ }
+ }
+ cur = static_cast(&arr.p->entries()[bucket]);
+ } else {
+ string string_key = keysp[j].to_string();
+ int64_t int_key = string_key.hash();
+ array_bucket *string_entries = arr.p->entries();
+ uint32_t bucket = arr.p->choose_bucket(int_key);
+ while (
+ (string_entries[bucket].int_key != int_key || string_entries[bucket].string_key.is_dummy_string() || string_entries[bucket].string_key != string_key)) {
+ if (++bucket == arr.p->buf_size) [[unlikely]] {
+ bucket = 0;
+ }
+ }
+ cur = static_cast(&string_entries[bucket]);
+ }
+
+ cur->prev = arr.p->get_pointer(prev);
+ prev->next = arr.p->get_pointer(cur);
+
+ prev = cur;
+ }
+ prev->next = arr.p->get_pointer(arr.p->end());
+ arr.p->end()->prev = arr.p->get_pointer(prev);
+}
template
concept convertible_to_php_bool = requires(T t) {
@@ -71,9 +254,7 @@ requires(std::invocable) task_t> f$array_filter(array a, Pr
co_return co_await std::invoke(std::move(pred), it.get_value());
});
} else {
- co_return co_await array_functions_impl_::array_filter_impl(a, [&pred](const auto &it) noexcept {
- return std::invoke(std::move(pred), it.get_value());
- });
+ co_return co_await array_functions_impl_::array_filter_impl(a, [&pred](const auto &it) noexcept { return std::invoke(std::move(pred), it.get_value()); });
}
}
@@ -223,19 +404,41 @@ array f$array_combine(const array &keys, const array &values) {
php_critical_error("call to unsupported function");
}
-template
-void f$usort(array &a, const T1 &compare) {
- php_critical_error("call to unsupported function");
+template
+requires(std::invocable) task_t f$usort(array &a, Comparator compare) {
+ if constexpr (is_async_function_v) {
+ /* ATTENTION: temporary copy is necessary since functions is coroutine and sort is inplace */
+ array tmp{a};
+ co_await array_functions_impl_::async_sort>(tmp, std::move(compare), true);
+ a = tmp;
+ co_return;
+ } else {
+ co_return a.sort(std::move(compare), true);
+ }
}
-template
-void f$uasort(array &a, const T1 &compare) {
- php_critical_error("call to unsupported function");
+template
+requires(std::invocable) task_t f$uasort(array &a, Comparator compare) {
+ if constexpr (is_async_function_v) {
+ /* ATTENTION: temporary copy is necessary since functions is coroutine and sort is inplace */
+ array tmp{a};
+ co_await array_functions_impl_::async_sort>(tmp, std::move(compare), false);
+ a = tmp;
+ } else {
+ co_return a.sort(std::move(compare), false);
+ }
}
-template
-void f$uksort(array &a, const T1 &compare) {
- php_critical_error("call to unsupported function");
+template
+requires(std::invocable::key_type, typename array::key_type>) task_t f$uksort(array &a, Comparator compare) {
+ if constexpr (is_async_function_v) {
+ /* ATTENTION: temporary copy is necessary since functions is coroutine and sort is inplace */
+ array tmp{a};
+ co_await array_functions_impl_::async_ksort>(tmp, std::move(compare), false);
+ a = tmp;
+ } else {
+ co_return a.ksort(std::move(compare));
+ }
}
template
diff --git a/runtime/array_functions.h b/runtime/array_functions.h
index e1d9fba1c2..5d60e7a352 100644
--- a/runtime/array_functions.h
+++ b/runtime/array_functions.h
@@ -720,3 +720,15 @@ T f$vk_dot_product(const array &a, const array &b) {
}
return vk_dot_product_sparse(a, b);
}
+
+template
+Result array_functions_impl_::async_sort([[maybe_unused]] array & arr, [[maybe_unused]] Comparator comparator, [[maybe_unused]] bool renumber) noexcept {
+ struct async_sort_stub_class {};
+ static_assert(std::is_same_v, "array async sort functions supported only in runtime light ");
+}
+
+template
+Result array_functions_impl_::async_ksort([[maybe_unused]] array & arr, [[maybe_unused]] Comparator comparator) noexcept {
+ struct async_ksort_stub_class {};
+ static_assert(std::is_same_v, "array async sort functions supported only in runtime light ");
+}
diff --git a/tests/phpt/cl/156_recursive_assumptions.php b/tests/phpt/cl/156_recursive_assumptions.php
index 5847ae8840..fe633e782f 100644
--- a/tests/phpt/cl/156_recursive_assumptions.php
+++ b/tests/phpt/cl/156_recursive_assumptions.php
@@ -1,4 +1,4 @@
-@ok k2_skip
+@ok