Skip to content

Commit 8078a9b

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 QOS_CLASS_USER_INTERACTIVE to QOS_CLASS_BACKGROUND.
1 parent 26c432d commit 8078a9b

File tree

5 files changed

+160
-4
lines changed

5 files changed

+160
-4
lines changed

ext/pcntl/config.m4

Lines changed: 1 addition & 1 deletion
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

Lines changed: 55 additions & 2 deletions
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,50 @@ PHP_FUNCTION(pcntl_getcpu)
16221631
}
16231632
#endif
16241633

1634+
#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
1635+
PHP_FUNCTION(pcntl_getqos_class)
1636+
{
1637+
qos_class_t qos_class;
1638+
zend_object *qos_obj;
1639+
1640+
ZEND_PARSE_PARAMETERS_NONE();
1641+
1642+
if (UNEXPECTED(pthread_get_qos_class_np(pthread_self(), &qos_class, NULL) != 0))
1643+
{
1644+
// unlikely unless an external tool set the QOS class with a wrong value
1645+
PCNTL_G(last_error) = errno;
1646+
zend_throw_error(NULL, "invalid QOS class %u", qos_class);
1647+
RETURN_THROWS();
1648+
}
1649+
1650+
if (UNEXPECTED(zend_enum_get_case_by_value(&qos_obj, QosClass_ce, (zend_long)qos_class, NULL, false) == FAILURE))
1651+
{
1652+
zend_throw_error(NULL, "invalid QOS value class entry %u", qos_class);
1653+
RETURN_THROWS();
1654+
}
1655+
1656+
RETVAL_OBJ(qos_obj);
1657+
}
1658+
1659+
PHP_FUNCTION(pcntl_setqos_class)
1660+
{
1661+
zval *qos_obj;
1662+
1663+
ZEND_PARSE_PARAMETERS_START(1, 1)
1664+
Z_PARAM_OBJECT_OF_CLASS(qos_obj, QosClass_ce)
1665+
ZEND_PARSE_PARAMETERS_END();
1666+
1667+
zend_long qos_class = Z_LVAL_P(zend_enum_fetch_case_value(Z_OBJ_P(qos_obj)));
1668+
1669+
if (pthread_set_qos_class_self_np((qos_class_t)qos_class, 0) != 0)
1670+
{
1671+
PCNTL_G(last_error) = errno;
1672+
zend_argument_value_error(1, "must be one of QosClass enum entries : ::UserInteractive, ::UserInitiated, ::Default, ::Utility or ::Background");
1673+
RETURN_THROWS();
1674+
}
1675+
}
1676+
#endif
1677+
16251678
static void pcntl_interrupt_function(zend_execute_data *execute_data)
16261679
{
16271680
pcntl_signal_dispatch();

ext/pcntl/pcntl.stub.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,18 @@ 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: int
1009+
{
1010+
case UserInteractive = 0x21;
1011+
case UserInitiated = 0x19;
1012+
case Default = 0x15;
1013+
case Utility = 0x11;
1014+
case Background = 0x09;
1015+
case Unspecified = 0x00;
1016+
}
1017+
1018+
function pcntl_getqos_class(): QosClass {}
1019+
function pcntl_setqos_class(QosClass $qos_class = QosClass::Default): void {}
1020+
#endif

ext/pcntl/pcntl_arginfo.h

Lines changed: 63 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/pcntl/tests/pcntl_qosclass.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
15+
try {
16+
pcntl_setqos_class(QosClass::Unspecified);
17+
} catch (\ValueError $e) {
18+
echo $e->getMessage() . PHP_EOL;
19+
}
20+
pcntl_setqos_class(QosClass::Background);
21+
var_dump(QosClass::Background == pcntl_getqos_class());
22+
?>
23+
--EXPECT--
24+
bool(true)
25+
pcntl_setqos_class(): Argument #1 ($qos_class) must be one of QosClass enum entries : ::UserInteractive, ::UserInitiated, ::Default, ::Utility or ::Background
26+
bool(true)

0 commit comments

Comments
 (0)