Skip to content

ext/pcntl: pcntl_getqos_class/pcntl_setqos_class addition. #13945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ext/pcntl/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if test "$PHP_PCNTL" != "no"; then
AC_CHECK_FUNCS([fork], [], [AC_MSG_ERROR([pcntl: fork() not supported by this platform])])
AC_CHECK_FUNCS([waitpid], [], [AC_MSG_ERROR([pcntl: waitpid() not supported by this platform])])
AC_CHECK_FUNCS([sigaction], [], [AC_MSG_ERROR([pcntl: sigaction() not supported by this platform])])
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open sched_setaffinity])
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open sched_setaffinity pthread_set_qos_class_self_np])

dnl if unsupported, -1 means automatically ENOSYS in this context
AC_MSG_CHECKING([if sched_getcpu is supported])
Expand Down
95 changes: 93 additions & 2 deletions ext/pcntl/pcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ typedef cpuset_t cpu_set_t;
#endif
#endif

#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
#include <pthread/qos.h>
static zend_class_entry *QosClass_ce;
#endif

#ifdef HAVE_PIDFD_OPEN
#include <sys/syscall.h>
#endif
Expand All @@ -65,10 +70,11 @@ typedef cpuset_t cpu_set_t;

#define LONG_CONST(c) (zend_long) c

#include "pcntl_arginfo.h"

#include "Zend/zend_enum.h"
#include "Zend/zend_max_execution_timer.h"

#include "pcntl_arginfo.h"

ZEND_DECLARE_MODULE_GLOBALS(pcntl)
static PHP_GINIT_FUNCTION(pcntl);

Expand Down Expand Up @@ -136,6 +142,9 @@ PHP_RINIT_FUNCTION(pcntl)

PHP_MINIT_FUNCTION(pcntl)
{
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
QosClass_ce = register_class_QosClass();
#endif
register_pcntl_symbols(module_number);
orig_interrupt_function = zend_interrupt_function;
zend_interrupt_function = pcntl_interrupt_function;
Expand Down Expand Up @@ -1622,6 +1631,88 @@ PHP_FUNCTION(pcntl_getcpu)
}
#endif

#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
static qos_class_t qos_zval_to_lval(const zval *qos_obj)
{
qos_class_t qos_class = QOS_CLASS_DEFAULT;
zend_string *entry = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(qos_obj)));

if (zend_string_equals_literal(entry, "UserInteractive")) {
qos_class = QOS_CLASS_USER_INTERACTIVE;
} else if (zend_string_equals_literal(entry, "UserInitiated")) {
qos_class = QOS_CLASS_USER_INITIATED;
} else if (zend_string_equals_literal(entry, "Utility")) {
qos_class = QOS_CLASS_UTILITY;
} else if (zend_string_equals_literal(entry, "Background")) {
qos_class = QOS_CLASS_BACKGROUND;
}

return qos_class;
}

static zend_object *qos_lval_to_zval(qos_class_t qos_class)
{
const char *entryname;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If macOS ever introduces more qos types, then the fact this is uninitialized but used below will be UB

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite apple has very little incentive to add more level of service, default is handled below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, you are right. I missed the default.

switch (qos_class)
{
case QOS_CLASS_USER_INTERACTIVE:
entryname = "UserInteractive";
break;
case QOS_CLASS_USER_INITIATED:
entryname = "UserInitiated";
break;
case QOS_CLASS_UTILITY:
entryname = "Utility";
break;
case QOS_CLASS_BACKGROUND:
entryname = "Background";
break;
case QOS_CLASS_DEFAULT:
default:
entryname = "Default";
break;
}

return zend_enum_get_case_cstr(QosClass_ce, entryname);
}

PHP_FUNCTION(pcntl_getqos_class)
{
qos_class_t qos_class;

ZEND_PARSE_PARAMETERS_NONE();

if (UNEXPECTED(pthread_get_qos_class_np(pthread_self(), &qos_class, NULL) != 0))
{
// unlikely unless an external tool set the QOS class with a wrong value
PCNTL_G(last_error) = errno;
zend_throw_error(NULL, "invalid QOS class %u", qos_class);
RETURN_THROWS();
}

RETURN_OBJ_COPY(qos_lval_to_zval(qos_class));
}

PHP_FUNCTION(pcntl_setqos_class)
{
zval *qos_obj;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJECT_OF_CLASS(qos_obj, QosClass_ce)
ZEND_PARSE_PARAMETERS_END();

qos_class_t qos_class = qos_zval_to_lval(qos_obj);

if (UNEXPECTED(pthread_set_qos_class_self_np((qos_class_t)qos_class, 0) != 0))
{
// unlikely, unless it is a new os issue, as we draw from the specified enum values
PCNTL_G(last_error) = errno;
zend_throw_error(NULL, "pcntl_setqos_class failed");
RETURN_THROWS();
}
}
#endif

static void pcntl_interrupt_function(zend_execute_data *execute_data)
{
pcntl_signal_dispatch();
Expand Down
14 changes: 14 additions & 0 deletions ext/pcntl/pcntl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -1003,3 +1003,17 @@ function pcntl_setcpuaffinity(?int $process_id = null, array $cpu_ids = []): boo
#ifdef HAVE_SCHED_GETCPU
function pcntl_getcpu(): int {}
#endif

#ifdef HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP
enum QosClass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be named Pcntl\QosClass as per https://wiki.php.net/rfc/namespaces_in_bundled_extensions.

{
case UserInteractive;
case UserInitiated;
case Default;
case Utility;
case Background;
}

function pcntl_getqos_class(): QosClass {}
function pcntl_setqos_class(QosClass $qos_class = QosClass::Default): void {}
#endif
50 changes: 49 additions & 1 deletion ext/pcntl/pcntl_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions ext/pcntl/tests/pcntl_qosclass.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
pcntl_getqos_class()/pcntl_setqos_class()
--EXTENSIONS--
pcntl
--SKIPIF--
<?php
if (!function_exists("pcntl_getqos_class")) die("skip pcntl_getqos_class() is not available");
if (getenv('SKIP_REPEAT')) die("skip Not repeatable");
?>
--FILE--
<?php
pcntl_setqos_class(QosClass::Default);
var_dump(QosClass::Default === pcntl_getqos_class());
pcntl_setqos_class(QosClass::Background);
var_dump(QosClass::Background == pcntl_getqos_class());
?>
--EXPECT--
bool(true)
bool(true)