diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index c4e70a50039de..073148a0a1d48 100644 --- a/ext/pcntl/config.m4 +++ b/ext/pcntl/config.m4 @@ -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]) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 6ccfd71f9ab62..1c09253f8dfd5 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -51,6 +51,11 @@ typedef cpuset_t cpu_set_t; #endif #endif +#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP) +#include +static zend_class_entry *QosClass_ce; +#endif + #ifdef HAVE_PIDFD_OPEN #include #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(); diff --git a/ext/pcntl/pcntl.stub.php b/ext/pcntl/pcntl.stub.php index 39f9db2d8b91c..cf1b5a4cced5f 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -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 diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index c51128f1a8a98..734fb3976e0af 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -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) @@ -272,8 +289,20 @@ static const zend_function_entry ext_functions[] = { #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) { @@ -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 diff --git a/ext/pcntl/tests/pcntl_qosclass.phpt b/ext/pcntl/tests/pcntl_qosclass.phpt new file mode 100644 index 0000000000000..61b4259288697 --- /dev/null +++ b/ext/pcntl/tests/pcntl_qosclass.phpt @@ -0,0 +1,19 @@ +--TEST-- +pcntl_getqos_class()/pcntl_setqos_class() +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true)