Skip to content

Commit d407266

Browse files
committed
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 phpGH-13945
1 parent 584a7b8 commit d407266

File tree

7 files changed

+186
-4
lines changed

7 files changed

+186
-4
lines changed

NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ PHP NEWS
132132
. Updated pcntl_get_signal_handler signal id upper limit to be
133133
more in line with platforms limits. (David Carlier)
134134
. Added pcntl_getcpu for Linux/FreeBSD. (David Carlier)
135+
. Added pcntl_getqos_class/pcntl_setqos_class for macOs. (David Carlier)
135136

136137
- PCRE:
137138
. Upgrade bundled pcre2lib to version 10.43. (nielsdos)

UPGRADING

+9
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ PHP 8.4 UPGRADE NOTES
475475
. Added pcntl_getaffinity to get the cpu(s) bound to a process and
476476
pcntl_setaffinity to bind 1 or more cpus to a process.
477477
. Added pcntl_getcpu to get the cpu id from where the current process runs.
478+
. Added pcntl_getqos_class to get the QoS level (aka performance and related
479+
energy consumption) of the current process and pcntl_setqos_class to set it.
478480

479481
- Sodium:
480482
. Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*()
@@ -582,6 +584,13 @@ PHP 8.4 UPGRADE NOTES
582584
. X509_PURPOSE_OCSP_HELPER.
583585
. X509_PURPOSE_TIMESTAMP_SIGN.
584586

587+
- PCNTL:
588+
. QosClass::Background (macOs only).
589+
. QosClass::Default (macOs only).
590+
. QosClass::UserInteractive (macOs only).
591+
. QosClass::UserInitiated (macOs only).
592+
. QosClass::Utility (macOs only).
593+
585594
- Standard:
586595
. PHP_ROUND_CEILING.
587596
. PHP_ROUND_FLOOR.

ext/pcntl/config.m4

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if test "$PHP_PCNTL" != "no"; then
77
AC_CHECK_FUNCS([fork], [], [AC_MSG_ERROR([pcntl: fork() not supported by this platform])])
88
AC_CHECK_FUNCS([waitpid], [], [AC_MSG_ERROR([pcntl: waitpid() not supported by this platform])])
99
AC_CHECK_FUNCS([sigaction], [], [AC_MSG_ERROR([pcntl: sigaction() not supported by this platform])])
10-
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open sched_setaffinity])
10+
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open sched_setaffinity pthread_set_qos_class_self_np])
1111

1212
dnl if unsupported, -1 means automatically ENOSYS in this context
1313
AC_MSG_CHECKING([if sched_getcpu is supported])

ext/pcntl/pcntl.c

+93-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ typedef cpuset_t cpu_set_t;
5151
#endif
5252
#endif
5353

54+
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
55+
#include <pthread/qos.h>
56+
static zend_class_entry *QosClass_ce;
57+
#endif
58+
5459
#ifdef HAVE_PIDFD_OPEN
5560
#include <sys/syscall.h>
5661
#endif
@@ -65,10 +70,11 @@ typedef cpuset_t cpu_set_t;
6570

6671
#define LONG_CONST(c) (zend_long) c
6772

68-
#include "pcntl_arginfo.h"
69-
73+
#include "Zend/zend_enum.h"
7074
#include "Zend/zend_max_execution_timer.h"
7175

76+
#include "pcntl_arginfo.h"
77+
7278
ZEND_DECLARE_MODULE_GLOBALS(pcntl)
7379
static PHP_GINIT_FUNCTION(pcntl);
7480

@@ -136,6 +142,9 @@ PHP_RINIT_FUNCTION(pcntl)
136142

137143
PHP_MINIT_FUNCTION(pcntl)
138144
{
145+
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
146+
QosClass_ce = register_class_QosClass();
147+
#endif
139148
register_pcntl_symbols(module_number);
140149
orig_interrupt_function = zend_interrupt_function;
141150
zend_interrupt_function = pcntl_interrupt_function;
@@ -1622,6 +1631,88 @@ PHP_FUNCTION(pcntl_getcpu)
16221631
}
16231632
#endif
16241633

1634+
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
1635+
static qos_class_t qos_zval_to_lval(const zval *qos_obj)
1636+
{
1637+
qos_class_t qos_class = QOS_CLASS_DEFAULT;
1638+
zend_string *entry = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(qos_obj)));
1639+
1640+
if (zend_string_equals_literal(entry, "UserInteractive")) {
1641+
qos_class = QOS_CLASS_USER_INTERACTIVE;
1642+
} else if (zend_string_equals_literal(entry, "UserInitiated")) {
1643+
qos_class = QOS_CLASS_USER_INITIATED;
1644+
} else if (zend_string_equals_literal(entry, "Utility")) {
1645+
qos_class = QOS_CLASS_UTILITY;
1646+
} else if (zend_string_equals_literal(entry, "Background")) {
1647+
qos_class = QOS_CLASS_BACKGROUND;
1648+
}
1649+
1650+
return qos_class;
1651+
}
1652+
1653+
static zend_object *qos_lval_to_zval(qos_class_t qos_class)
1654+
{
1655+
const char *entryname;
1656+
switch (qos_class)
1657+
{
1658+
case QOS_CLASS_USER_INTERACTIVE:
1659+
entryname = "UserInteractive";
1660+
break;
1661+
case QOS_CLASS_USER_INITIATED:
1662+
entryname = "UserInitiated";
1663+
break;
1664+
case QOS_CLASS_UTILITY:
1665+
entryname = "Utility";
1666+
break;
1667+
case QOS_CLASS_BACKGROUND:
1668+
entryname = "Background";
1669+
break;
1670+
case QOS_CLASS_DEFAULT:
1671+
default:
1672+
entryname = "Default";
1673+
break;
1674+
}
1675+
1676+
return zend_enum_get_case_cstr(QosClass_ce, entryname);
1677+
}
1678+
1679+
PHP_FUNCTION(pcntl_getqos_class)
1680+
{
1681+
qos_class_t qos_class;
1682+
1683+
ZEND_PARSE_PARAMETERS_NONE();
1684+
1685+
if (UNEXPECTED(pthread_get_qos_class_np(pthread_self(), &qos_class, NULL) != 0))
1686+
{
1687+
// unlikely unless an external tool set the QOS class with a wrong value
1688+
PCNTL_G(last_error) = errno;
1689+
zend_throw_error(NULL, "invalid QOS class %u", qos_class);
1690+
RETURN_THROWS();
1691+
}
1692+
1693+
RETURN_OBJ_COPY(qos_lval_to_zval(qos_class));
1694+
}
1695+
1696+
PHP_FUNCTION(pcntl_setqos_class)
1697+
{
1698+
zval *qos_obj;
1699+
1700+
ZEND_PARSE_PARAMETERS_START(1, 1)
1701+
Z_PARAM_OBJECT_OF_CLASS(qos_obj, QosClass_ce)
1702+
ZEND_PARSE_PARAMETERS_END();
1703+
1704+
qos_class_t qos_class = qos_zval_to_lval(qos_obj);
1705+
1706+
if (UNEXPECTED(pthread_set_qos_class_self_np((qos_class_t)qos_class, 0) != 0))
1707+
{
1708+
// unlikely, unless it is a new os issue, as we draw from the specified enum values
1709+
PCNTL_G(last_error) = errno;
1710+
zend_throw_error(NULL, "pcntl_setqos_class failed");
1711+
RETURN_THROWS();
1712+
}
1713+
}
1714+
#endif
1715+
16251716
static void pcntl_interrupt_function(zend_execute_data *execute_data)
16261717
{
16271718
pcntl_signal_dispatch();

ext/pcntl/pcntl.stub.php

+14
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,17 @@ function pcntl_setcpuaffinity(?int $process_id = null, array $cpu_ids = []): boo
10031003
#ifdef HAVE_SCHED_GETCPU
10041004
function pcntl_getcpu(): int {}
10051005
#endif
1006+
1007+
#ifdef HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP
1008+
enum QosClass
1009+
{
1010+
case UserInteractive;
1011+
case UserInitiated;
1012+
case Default;
1013+
case Utility;
1014+
case Background;
1015+
}
1016+
1017+
function pcntl_getqos_class(): QosClass {}
1018+
function pcntl_setqos_class(QosClass $qos_class = QosClass::Default): void {}
1019+
#endif

ext/pcntl/pcntl_arginfo.h

+49-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/pcntl/tests/pcntl_qosclass.phpt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
pcntl_getqos_class()/pcntl_setqos_class()
3+
--EXTENSIONS--
4+
pcntl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("pcntl_getqos_class")) die("skip pcntl_getqos_class() is not available");
8+
if (getenv('SKIP_REPEAT')) die("skip Not repeatable");
9+
?>
10+
--FILE--
11+
<?php
12+
pcntl_setqos_class(QosClass::Default);
13+
var_dump(QosClass::Default === pcntl_getqos_class());
14+
pcntl_setqos_class(QosClass::Background);
15+
var_dump(QosClass::Background == pcntl_getqos_class());
16+
?>
17+
--EXPECT--
18+
bool(true)
19+
bool(true)

0 commit comments

Comments
 (0)