Skip to content

Commit

Permalink
Fix usage of resumable function with ArrayAccess methods (#1198)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkornaukhov03 authored Jan 16, 2025
1 parent 23ce77b commit 95a09ea
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 5 deletions.
29 changes: 24 additions & 5 deletions compiler/pipes/calc-bad-vars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "compiler/pipes/calc-bad-vars.h"

#include "compiler/kphp_assert.h"
#include <algorithm>
#include <queue>
#include <vector>
Expand All @@ -13,6 +12,7 @@
#include "compiler/data/class-data.h"
#include "compiler/data/src-file.h"
#include "compiler/function-pass.h"
#include "compiler/kphp_assert.h"
#include "compiler/pipes/calc-func-dep.h"
#include "compiler/utils/idmap.h"

Expand Down Expand Up @@ -549,10 +549,10 @@ class CalcBadVars {
for (const auto &func : call_graph.functions) {
if (into_interruptible[func]) {
func->is_interruptible = true;
if (unlikely(func->class_id && func->class_id->is_required_interface)) {
std::string func_name = func->name;
std::replace(func_name.begin(), func_name.end(), '$', ':');
kphp_error(false, fmt_format("{} cannot be interruptible", func_name).c_str());
if (unlikely(func->class_id && func->class_id == G->get_class("ArrayAccess"))) {
kphp_error(false, fmt_format("Function [{}] is a method of ArrayAccess, it cannot call interruptible functions\n"
"Function transitively calls the interruptible function along the following chain:\n{}\n",
func->as_human_readable(), get_call_path(func, to_parents)));
}
}
}
Expand Down Expand Up @@ -591,7 +591,13 @@ class CalcBadVars {
func->wait_prev = to_parents[func];
}
}

for (const auto &func : call_graph.functions) {
if (func->class_id && func->class_id == G->get_class("ArrayAccess") && func->can_be_implicitly_interrupted_by_other_resumable) {
kphp_error(false, fmt_format("Function [{}] is a method of ArrayAccess, it cannot call resumable functions\n"
"Function transitively calls the resumable function along the following chain:\n{}\n",
func->as_human_readable(), get_call_path(func, to_parents)));
}
if (func->is_resumable) {
if (func->should_be_sync) {
kphp_error (0, fmt_format("Function [{}] marked with @kphp-sync, but turn up to be resumable\n"
Expand Down Expand Up @@ -637,6 +643,19 @@ class CalcBadVars {
my_unique(&main_dep);
}

static std::string get_call_path(FunctionPtr func, const IdMap<FunctionPtr> &to_parents) {
kphp_assert_msg(func, "Unexpected null FunctionPtr in getting call-chain");
std::vector<std::string> names;
names.push_back(TermStringFormat::paint(func->as_human_readable(), TermStringFormat::red));

func = to_parents[func];
while (func) {
names.push_back(func->as_human_readable());
func = to_parents[func];
}
return vk::join(names, " -> ");
};

class MergeRefVarsCallback : public MergeReachalbeCallback<VarPtr> {
private:
const IdMap<std::vector<VarPtr>> &to_merge_;
Expand Down
34 changes: 34 additions & 0 deletions tests/phpt/array_access/014_another_method_forked.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@ok
<?php
require_once 'kphp_tester_include.php';

function like_rpc_write() {
echo "log\n";
sched_yield(); // resumable
}

class A implements \ArrayAccess {
public function offsetSet($offset, $value) {}
/** @return bool */
public function offsetExists($offset) {return true;}
public function offsetUnset($offset) {}
/** @return mixed */
public function offsetGet($offset) {
return null;
}

public function to_be_forked() {
echo "in to_be_forked\n";
like_rpc_write();
return null;
}
}

function test() {
$x = new A();

$fut = fork($x->to_be_forked());
wait($fut);
}

test();
29 changes: 29 additions & 0 deletions tests/phpt/array_access/103_calls_resumable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@kphp_should_fail
/Function \[ArrayAccess::offsetGet\] is a method of ArrayAccess, it cannot call (resumable|interruptible) functions/
/Function transitively calls the (resumable|interruptible) function along the following chain:/
/ArrayAccess::offsetGet -> A::offsetGet -> like_rpc_write -> sched_yield/

<?php

function like_rpc_write() {
printf("log\n");
sched_yield(); // resumable
}

class A implements \ArrayAccess {
public function offsetSet($offset, $value) {}
/** @return bool */
public function offsetExists($offset) {return true;}
public function offsetUnset($offset) {}
/** @return mixed */
public function offsetGet($offset) {
like_rpc_write();
return null;
}
}

function test() {
$x = new A();
}

test();
24 changes: 24 additions & 0 deletions tests/phpt/array_access/104_forked.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@kphp_should_fail
/Function \[ArrayAccess::offsetGet\] is a method of ArrayAccess, it cannot call (resumable|interruptible) functions/
/Function transitively calls the (resumable|interruptible) function along the following chain:/
/ArrayAccess::offsetGet -> A::offsetGet/

<?php

class A implements \ArrayAccess {
public function offsetSet($offset, $value) {}
/** @return bool */
public function offsetExists($offset) {return true;}
public function offsetUnset($offset) {}
/** @return mixed */
public function offsetGet($offset) {return null;}
}

function test() {
$x = new A();
$y = fork($x->offsetGet(42));

wait($y);
}

test();

0 comments on commit 95a09ea

Please sign in to comment.