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

Conversation

devnexen
Copy link
Member

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.

@devnexen devnexen force-pushed the pcntl_thread_qos_macos branch 2 times, most recently from a256774 to bdab6c1 Compare April 11, 2024 21:31
@devnexen devnexen marked this pull request as ready for review April 11, 2024 21:59
@devnexen devnexen requested a review from kocsismate as a code owner April 11, 2024 21:59
RETURN_THROWS();
}

RETURN_TRUE;
Copy link
Member

Choose a reason for hiding this comment

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

Why not just return NULL/void?

Copy link
Member Author

Choose a reason for hiding this comment

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

to give a success status at least ?

Copy link
Member

Choose a reason for hiding this comment

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

Is it really that useful if the function can only ever return true? I know a lot of other functions in pcntl only return true nowadays, but that's because they used to return false for ZPP failures or cases that have been promoted to Errors.

Comment on lines 1636 to 1648
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_value_error("invalid QOS class %u", qos_class);
RETURN_THROWS();
}
Copy link
Member

Choose a reason for hiding this comment

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

How can this happen? Also this doesn't look like a ValueError as no user input is taken.

Copy link
Member Author

Choose a reason for hiding this comment

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

true

if (pthread_set_qos_class_self_np((qos_class_t)qos_class, 0) != 0)
{
PCNTL_G(last_error) = errno;
zend_argument_value_error(1, "must be between QOS_CLASS_USER_INTERACTIVE and QOS_CLASS_BLACKGROUND");
Copy link
Member

Choose a reason for hiding this comment

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

Is that flag a bitmask? Or just individual settings, in the latter case I'd list them all rather than making the user guess the "underlying" int value of the constants.

Copy link
Member Author

Choose a reason for hiding this comment

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

individual. also the list had not been updated since a long time (and no reason to do so anyway) so yes .. I ll update.

@devnexen devnexen force-pushed the pcntl_thread_qos_macos branch from bdab6c1 to 1ec8a4a Compare April 14, 2024 11:25
Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

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

Last wording nit, but looks good to me otherwise :)

if (pthread_set_qos_class_self_np((qos_class_t)qos_class, 0) != 0)
{
PCNTL_G(last_error) = errno;
zend_argument_value_error(1, "must be between QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_USER_INITIATED, QOS_CLASS_DEFAULT, QOS_CLASS_UTILITY and QOS_CLASS_BACKGROUND");
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
zend_argument_value_error(1, "must be between QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_USER_INITIATED, QOS_CLASS_DEFAULT, QOS_CLASS_UTILITY and QOS_CLASS_BACKGROUND");
zend_argument_value_error(1, "must be one of QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_USER_INITIATED, QOS_CLASS_DEFAULT, QOS_CLASS_UTILITY, or QOS_CLASS_BACKGROUND");

@devnexen devnexen force-pushed the pcntl_thread_qos_macos branch from 1ec8a4a to 376bdd9 Compare April 14, 2024 12:18
@nielsdos
Copy link
Member

I'm gonna be "that guy", and ask: should we start introducing enums instead of constants for new functions? I see a bunch of QOS constants being added here, but using enums instead would provide better type safety, and an improved DX because the function "documents itself" (i.e. the IDE and SA tools can help you better).
Using enums isn't always viable of course if it's just adding more constants of a kind that already existed, but the QOS ones look entirely new.

@Girgias
Copy link
Member

Girgias commented Apr 14, 2024

Actually, that's a good point :D

@devnexen
Copy link
Member Author

You want something like

enum QosClass {
     case UserInitiated;
}

like zig like to do ?
Why not generalising for other (e.g. pgsql has a bunch of constants which are enums) for php 9 ?

@nielsdos
Copy link
Member

Indeed something like that.

Why not generalising for other (e.g. pgsql has a bunch of constants which are enums) for php 9 ?

That could be PHP9 material.
The point I'm trying to make is that for new code we could use enums and keep the constants for old code. Then in a future PHP version convert the old constants to enums. We could even have a transition period in the future where we already introduce the enum but still allow the constant to be passed. Something like function foo(int|MyEnum $option).

@devnexen
Copy link
Member Author

Ok I ll it a go a bit later.

Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

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

I like this API much better, some remarks.

@@ -53,6 +53,7 @@ typedef cpuset_t cpu_set_t;

#if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP)
#include <pthread/qos.h>
PHPAPI zend_class_entry *QosClass_ce;
Copy link
Member

Choose a reason for hiding this comment

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

Why is this exposed via PHPAPI? Do we expect other extensions to reuse this enum?

Copy link
Member Author

Choose a reason for hiding this comment

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

I do not think no :)

if (pthread_set_qos_class_self_np((qos_class_t)qos_class, 0) != 0)
{
PCNTL_G(last_error) = errno;
zend_argument_value_error(1, "must be one of QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_USER_INITIATED, QOS_CLASS_DEFAULT, QOS_CLASS_UTILITY and QOS_CLASS_BACKGROUND");
zend_argument_value_error(1, "must be one of QosClass enum entries : ::UserInteractive, ::UserInitiated, ::Default, ::Utility and ::Background");
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
zend_argument_value_error(1, "must be one of QosClass enum entries : ::UserInteractive, ::UserInitiated, ::Default, ::Utility and ::Background");
zend_argument_value_error(1, "must be one of QosClass enum entries : ::UserInteractive, ::UserInitiated, ::Default, ::Utility or ::Background");

@@ -58,4 +58,6 @@ ZEND_TSRMLS_CACHE_EXTERN()
ZEND_EXTERN_MODULE_GLOBALS(pcntl)
#define PCNTL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(pcntl, v)

extern PHPAPI zend_class_entry *QosClass_ce;
Copy link
Member

Choose a reason for hiding this comment

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

Ditto

RETURN_THROWS();
}

ZVAL_COPY_OR_DUP(return_value, zend_enum_fetch_case_value(qos_obj));
Copy link
Member

Choose a reason for hiding this comment

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

zend_enum_fetch_case_value returns the backed value, but you want the object instead.

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.
@devnexen devnexen force-pushed the pcntl_thread_qos_macos branch from 85df17c to 8078a9b Compare April 14, 2024 16:44
Comment on lines 1008 to 1016
enum QosClass: int
{
case UserInteractive = 0x21;
case UserInitiated = 0x19;
case Default = 0x15;
case Utility = 0x11;
case Background = 0x09;
case Unspecified = 0x00;
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't think it should be a backed enum, exposing those values to userland is not really helpful IMHO. Also what is this Unspecified case about?

Comment on lines 1671 to 1673
PCNTL_G(last_error) = errno;
zend_argument_value_error(1, "must be one of QosClass enum entries : ::UserInteractive, ::UserInitiated, ::Default, ::Utility or ::Background");
RETURN_THROWS();
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 never happen if we are requiring an enum.

Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

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

Makes sense to me

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_cstr(entry, "UserInteractive", sizeof("UserInteractive") - 1)) {
Copy link
Member

Choose a reason for hiding this comment

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

Please use zend_string_equals_literal for these instead, it's less error-prone.

Copy link
Member Author

Choose a reason for hiding this comment

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

alright


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.

Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@devnexen devnexen closed this in d407266 Apr 15, 2024
@@ -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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants