diff --git a/builtin-functions/_functions.txt b/builtin-functions/_functions.txt index ef816fff19..501bfd28ed 100644 --- a/builtin-functions/_functions.txt +++ b/builtin-functions/_functions.txt @@ -959,7 +959,7 @@ function rpc_send (\RpcConnection $rpc_conn, $timeout ::: float = -1.0) ::: int; function rpc_send_noflush (\RpcConnection $rpc_conn, $timeout ::: float = -1.0) ::: int; function rpc_flush () ::: void; /** @kphp-extern-func-info resumable */ -function rpc_get ($request_id ::: int) ::: string | false; +function rpc_get ($request_id ::: int, $is_sync ::: bool = false) ::: string | false; function rpc_get_synchronously ($request_id ::: int) ::: string | false; /** @kphp-extern-func-info resumable */ function rpc_get_and_parse ($request_id ::: int) ::: bool; diff --git a/compiler/code-gen/vertex-compiler.cpp b/compiler/code-gen/vertex-compiler.cpp index 6f50d95b45..f8225cd3c5 100644 --- a/compiler/code-gen/vertex-compiler.cpp +++ b/compiler/code-gen/vertex-compiler.cpp @@ -970,6 +970,10 @@ void compile_fork(VertexAdaptor root, CodeGenerator &W) { compile_func_call(root->func_call(), W, func_call_mode::fork_call); } +void compile_force_sync(VertexAdaptor root, CodeGenerator &W) { + compile_func_call(root->func_call(), W, func_call_mode::async_call); +} + void compile_async(VertexAdaptor root, CodeGenerator &W) { auto func_call = root->func_call(); if (root->has_save_var()) { @@ -2161,6 +2165,9 @@ void compile_common_op(VertexPtr root, CodeGenerator &W) { case op_fork: compile_fork(root.as(), W); break; + case op_force_sync: + compile_force_sync(root.as(), W); + break; case op_async: compile_async(root.as(), W); break; diff --git a/compiler/compiler.cmake b/compiler/compiler.cmake index 636aaaa6d6..a8acedd379 100644 --- a/compiler/compiler.cmake +++ b/compiler/compiler.cmake @@ -143,6 +143,7 @@ prepend(KPHP_COMPILER_PIPES_SOURCES pipes/ filter-only-actually-used.cpp final-check.cpp fix-returns.cpp + force-sync.cpp gen-tree-postprocess.cpp generate-virtual-methods.cpp inline-defines-usages.cpp diff --git a/compiler/compiler.cpp b/compiler/compiler.cpp index 98ff1101e1..124fe98c2d 100644 --- a/compiler/compiler.cpp +++ b/compiler/compiler.cpp @@ -42,6 +42,7 @@ #include "compiler/pipes/check-classes.h" #include "compiler/pipes/check-conversions.h" #include "compiler/pipes/check-func-calls-and-vararg.h" +#include "compiler/pipes/force-sync.h" #include "compiler/pipes/check-modifications-of-const-vars.h" #include "compiler/pipes/check-nested-foreach.h" #include "compiler/pipes/wait-for-all-classes.h" @@ -259,6 +260,7 @@ bool compiler_execute(CompilerSettings *settings) { >> SyncC{} >> PipeC{} >> PassC{} + >> PassC{} >> PassC{} >> PipeC{} >> PipeC{} diff --git a/compiler/pipes/calc-rl.cpp b/compiler/pipes/calc-rl.cpp index 7684904e57..a47b2dceec 100644 --- a/compiler/pipes/calc-rl.cpp +++ b/compiler/pipes/calc-rl.cpp @@ -43,6 +43,9 @@ void rl_func_call_calc(VertexPtr root, RLValueType expected_rl_type) { case op_fork: rl_calc_all(root); return; + case op_force_sync: + rl_calc_all(root); + return; case op_array: //TODO: in fact it is wrong case op_tuple: case op_shape: diff --git a/compiler/pipes/cfg.cpp b/compiler/pipes/cfg.cpp index d560979389..30053904f9 100644 --- a/compiler/pipes/cfg.cpp +++ b/compiler/pipes/cfg.cpp @@ -645,6 +645,10 @@ void CFG::create_cfg(VertexPtr tree_node, Node *res_start, Node *res_finish, boo create_cfg(tree_node.as()->func_call(), res_start, res_finish); break; } + case op_force_sync: { + create_cfg(tree_node.as()->func_call(), res_start, res_finish); + break; + } case op_return: { auto return_op = tree_node.as(); if (return_op->has_expr()) { diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 9ac0736151..f33378b734 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -21,6 +21,8 @@ VertexPtr CheckFuncCallsAndVarargPass::on_enter_vertex(VertexPtr root) { return on_func_call(root.as()); } else if (root->type() == op_fork) { return on_fork(root.as()); + } else if (root->type() == op_force_sync) { + return on_force_sync(root.as()); } return root; @@ -317,3 +319,11 @@ VertexPtr CheckFuncCallsAndVarargPass::on_fork(VertexAdaptor v_fork) { "Invalid fork() usage: it must be called with exactly one func call inside, e.g. fork(f(...))"); return v_fork; } + +// check that force_sync(...) is called correctly, as force_sync(f()) +// note, that we do this after replacing op_invoke_call with op_func_call, not earlier +VertexPtr CheckFuncCallsAndVarargPass::on_force_sync(VertexAdaptor v_force_sync) { + kphp_error(v_force_sync->size() == 1 && (*v_force_sync->begin())->type() == op_func_call, + "Invalid force_sync() usage: it must be called with exactly one func call inside, e.g. force_sync(f(...))"); + return v_force_sync; +} diff --git a/compiler/pipes/check-func-calls-and-vararg.h b/compiler/pipes/check-func-calls-and-vararg.h index 30f8e65ede..93cb0ea370 100644 --- a/compiler/pipes/check-func-calls-and-vararg.h +++ b/compiler/pipes/check-func-calls-and-vararg.h @@ -26,4 +26,5 @@ class CheckFuncCallsAndVarargPass final : public FunctionPassBase { VertexPtr on_func_call(VertexAdaptor call); VertexPtr on_fork(VertexAdaptor v_fork); + VertexPtr on_force_sync(VertexAdaptor v_force_sync); }; diff --git a/compiler/pipes/extract-async.h b/compiler/pipes/extract-async.h index 41c9bf08e0..7e956f93e4 100644 --- a/compiler/pipes/extract-async.h +++ b/compiler/pipes/extract-async.h @@ -17,7 +17,7 @@ class ExtractAsyncPass final : public FunctionPassBase { VertexPtr on_exit_vertex(VertexPtr vertex) override; bool user_recursion(VertexPtr vertex) override { - return vertex->type() == op_fork; + return vertex->type() == op_fork || vertex->type() == op_force_sync; } private: diff --git a/compiler/pipes/force-sync.cpp b/compiler/pipes/force-sync.cpp new file mode 100644 index 0000000000..30ef66735c --- /dev/null +++ b/compiler/pipes/force-sync.cpp @@ -0,0 +1,25 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2024 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#include "compiler/pipes/force-sync.h" + +#include "compiler/compiler-core.h" +#include "compiler/inferring/public.h" +#include "compiler/data/class-data.h" +#include "compiler/function-pass.h" + +VertexPtr ForceSyncPass::on_enter_vertex(VertexPtr v) { + return v; +} + +bool ForceSyncPass::user_recursion(VertexPtr v) { + if (auto force_sync_v = v.try_as()) { + inside_force_sync++; + run_function_pass(force_sync_v->func_call_ref(), this); + inside_force_sync--; + return true; + } + + return false; +} \ No newline at end of file diff --git a/compiler/pipes/force-sync.h b/compiler/pipes/force-sync.h new file mode 100644 index 0000000000..68c33555c5 --- /dev/null +++ b/compiler/pipes/force-sync.h @@ -0,0 +1,23 @@ + +#pragma once + +#include + +#include "compiler/function-pass.h" +#include "compiler/threading/data-stream.h" + +class ForceSyncPass final : public FunctionPassBase { + private: + int inside_force_sync = 0; + + public: + std::string get_description() override { + return "Process force_sync() calls"; + } + + VertexPtr on_enter_vertex(VertexPtr v) override; + + bool user_recursion(VertexPtr v) override; + +}; + diff --git a/compiler/pipes/gen-tree-postprocess.cpp b/compiler/pipes/gen-tree-postprocess.cpp index ddd9b2a94b..804c8adc28 100644 --- a/compiler/pipes/gen-tree-postprocess.cpp +++ b/compiler/pipes/gen-tree-postprocess.cpp @@ -142,6 +142,7 @@ GenTreePostprocessPass::builtin_fun GenTreePostprocessPass::get_builtin_function {"not_false", {op_conv_drop_false, 1}}, {"not_null", {op_conv_drop_null, 1}}, {"fork", {op_fork, 1}}, + {"force_sync", {op_force_sync, 1}}, {"pow", {op_pow, 2}} }; auto it = functions.find(name); diff --git a/compiler/vertex-desc.json b/compiler/vertex-desc.json index 493011e3c0..2280adbb10 100644 --- a/compiler/vertex-desc.json +++ b/compiler/vertex-desc.json @@ -1920,6 +1920,23 @@ "str": "fork" } }, + { + "comment": "artificial op for a force_sync call; func_call() contains a force_sync argument", + "sons": { + "func_call": { + "id": 0, + "type": "op_func_call" + } + }, + "name": "op_force_sync", + "base_name": "meta_op_base", + "props": { + "rl": "rl_func", + "cnst": "cnst_nonconst_func", + "type": "common_op", + "str": "force_sync" + } + }, { "comment": "artificial op that wraps async function call, optionally with save_var() for LHS", "sons": { diff --git a/runtime/rpc.cpp b/runtime/rpc.cpp index d476cc54e0..00dcb07d43 100644 --- a/runtime/rpc.cpp +++ b/runtime/rpc.cpp @@ -950,7 +950,8 @@ bool drop_tl_query_info(int64_t query_id) { return true; } -Optional f$rpc_get(int64_t request_id, double timeout) { +Optional f$rpc_get(int64_t request_id, double timeout, bool is_sync) { + (void)is_sync; if (!drop_tl_query_info(request_id)) { return false; } diff --git a/runtime/rpc.h b/runtime/rpc.h index 7cf9844758..4b1b43c983 100644 --- a/runtime/rpc.h +++ b/runtime/rpc.h @@ -207,7 +207,7 @@ int64_t f$rpc_send_noflush(const class_instance &conn, double t void f$rpc_flush(); -Optional f$rpc_get(int64_t request_id, double timeout = -1.0); +Optional f$rpc_get(int64_t request_id, double timeout = -1.0, bool is_sync = false); Optional f$rpc_get_synchronously(int64_t request_id);