1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

ext/pcntl: pcntl_getqos_class/pcntl_setqos_class addition.

Introducting macOs Quality Of Service through those two calls.
on macOs arm64/M*, there is no concept of individual cores, thus
the old thread policy for cpu affinity does not work here.
Instead, the user can apply to the current process the level of
 performance/energy consumption they wish from the highest
QosClass::UserInteractive to QosClass::Background.

Close GH-13945
This commit is contained in:
David Carlier
2024-04-11 21:14:22 +01:00
parent 584a7b8e78
commit d40726670f
7 changed files with 186 additions and 4 deletions

1
NEWS
View File

@@ -132,6 +132,7 @@ PHP NEWS
. Updated pcntl_get_signal_handler signal id upper limit to be
more in line with platforms limits. (David Carlier)
. Added pcntl_getcpu for Linux/FreeBSD. (David Carlier)
. Added pcntl_getqos_class/pcntl_setqos_class for macOs. (David Carlier)
- PCRE:
. Upgrade bundled pcre2lib to version 10.43. (nielsdos)

View File

@@ -475,6 +475,8 @@ PHP 8.4 UPGRADE NOTES
. Added pcntl_getaffinity to get the cpu(s) bound to a process and
pcntl_setaffinity to bind 1 or more cpus to a process.
. Added pcntl_getcpu to get the cpu id from where the current process runs.
. Added pcntl_getqos_class to get the QoS level (aka performance and related
energy consumption) of the current process and pcntl_setqos_class to set it.
- Sodium:
. Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*()
@@ -582,6 +584,13 @@ PHP 8.4 UPGRADE NOTES
. X509_PURPOSE_OCSP_HELPER.
. X509_PURPOSE_TIMESTAMP_SIGN.
- PCNTL:
. QosClass::Background (macOs only).
. QosClass::Default (macOs only).
. QosClass::UserInteractive (macOs only).
. QosClass::UserInitiated (macOs only).
. QosClass::Utility (macOs only).
- Standard:
. PHP_ROUND_CEILING.
. PHP_ROUND_FLOOR.

View File

@@ -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])

View File

@@ -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
@@ -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);
@@ -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;
@@ -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;
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();

View File

@@ -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
{
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

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 75eacf08a17e18c30fb2111bb742c36b18aa9ead */
* Stub hash: 3e15bebb568e6e2031acbd932d6eefbd23984c83 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_fork, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()
@@ -157,6 +157,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_getcpu, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()
#endif
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_pcntl_getqos_class, 0, 0, QosClass, 0)
ZEND_END_ARG_INFO()
#endif
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_setqos_class, 0, 0, IS_VOID, 0)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, qos_class, QosClass, 0, "QosClass::Default")
ZEND_END_ARG_INFO()
#endif
ZEND_FUNCTION(pcntl_fork);
ZEND_FUNCTION(pcntl_waitpid);
ZEND_FUNCTION(pcntl_wait);
@@ -213,6 +224,12 @@ ZEND_FUNCTION(pcntl_setcpuaffinity);
#if defined(HAVE_SCHED_GETCPU)
ZEND_FUNCTION(pcntl_getcpu);
#endif
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
ZEND_FUNCTION(pcntl_getqos_class);
#endif
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
ZEND_FUNCTION(pcntl_setqos_class);
#endif
static const zend_function_entry ext_functions[] = {
ZEND_FE(pcntl_fork, arginfo_pcntl_fork)
@@ -271,10 +288,22 @@ static const zend_function_entry ext_functions[] = {
#endif
#if defined(HAVE_SCHED_GETCPU)
ZEND_FE(pcntl_getcpu, arginfo_pcntl_getcpu)
#endif
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
ZEND_FE(pcntl_getqos_class, arginfo_pcntl_getqos_class)
#endif
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
ZEND_FE(pcntl_setqos_class, arginfo_pcntl_setqos_class)
#endif
ZEND_FE_END
};
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
static const zend_function_entry class_QosClass_methods[] = {
ZEND_FE_END
};
#endif
static void register_pcntl_symbols(int module_number)
{
#if defined(WNOHANG)
@@ -628,3 +657,22 @@ static void register_pcntl_symbols(int module_number)
REGISTER_LONG_CONSTANT("PCNTL_ECAPMODE", ECAPMODE, CONST_PERSISTENT);
#endif
}
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
static zend_class_entry *register_class_QosClass(void)
{
zend_class_entry *class_entry = zend_register_internal_enum("QosClass", IS_UNDEF, class_QosClass_methods);
zend_enum_add_case_cstr(class_entry, "UserInteractive", NULL);
zend_enum_add_case_cstr(class_entry, "UserInitiated", NULL);
zend_enum_add_case_cstr(class_entry, "Default", NULL);
zend_enum_add_case_cstr(class_entry, "Utility", NULL);
zend_enum_add_case_cstr(class_entry, "Background", NULL);
return class_entry;
}
#endif

View File

@@ -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)