Skip to content

Make vm_interrupt and timed_out atomic #8327

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

Merged
merged 5 commits into from
Jun 1, 2022

Conversation

morrisonlevi
Copy link
Contributor

@morrisonlevi morrisonlevi commented Apr 8, 2022

On Windows these are already set from another thread and should be
atomic. Only one of these was previously atomic using
InterlockedExchange8. On Linux, it's unclear if to me if it should be
atomic based on existing code.

However, extensions which use vm interrupts are very likely to do the
same thing that Windows is doing: set a vm_interrupt from another
thread.

This adds a new zend_atomic_bool type. The type definition is only
available for compiler alignment and size info; it should be treated as
opaque and only the zend_atomic_bool_* family of functions should be
used. Extensions inspecting EG(vm_interrupt) and EG(timed_out) need
to be updated.

Note that directly using atomic_bool is complicated. All C++ stdlibs
that I checked typedef atomic_bool to std::atomic, which can't
be used in an extern "C" section, and there's at least one usage of
this in core, and probably more outside of it.

So, instead use platform specific functions, preferring compiler
intrinsics.

@morrisonlevi
Copy link
Contributor Author

I'm trying with compiler intrinsics now. I suspect I may need to determine if I need to link with -latomic sometimes.

Copy link
Member

@cmb69 cmb69 left a comment

Choose a reason for hiding this comment

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

Thank you! The Windows part looks good to me.

Copy link
Member

@nikic nikic left a comment

Choose a reason for hiding this comment

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

As I've told you many times before, please only use static inline in php-src, not inline or extern inline. I'm not having this discussion again.

@morrisonlevi
Copy link
Contributor Author

morrisonlevi commented Apr 13, 2022

@nikic I think given the past discussions, you may have misunderstood the point of inline + extern inline on these specific functions. So, here are the goals:

  1. I want there to be ZEND_API functions here which are callable from places such as extensions written in Rust.
  2. For performance reasons, we want these same functions to inline whenever possible within the engine itself, hence inline.

A quick grep (which means it may miss some cases with strange formatting, macros, or multiline definitions) show that within the engine today, this is novel: nothing is marked inline that is also ZEND_API.

I tested on a modified zend_atomic.{c,h} on Mac saw that static inline ZEND_API means that the functions with external linkage don't exist. You can verify this with a reduced example:

__attribute__((visibility("default")))
static inline int four(void) {
  return 4;
}

extern inline int four(void); // does nothing in this case

__attribute__((visibility("default")))
inline int five(void) {
  return 5;
}

extern inline int five(void); // without this, five won't have external linkage either

int main() { return four() == 4 && five() == 5 ? 0 : 1; }

I built with clang -fvisibility=hidden -O2 -g -Wall -Wextra inline.c -o inline and checked with nm -g inline. Here are the symbols:

0000000100000000 T __mh_execute_header
0000000100003fa0 T _five
                 U dyld_stub_binder

So, that's why I used non-static inline + extern inline. When discussing this a bit with bwoebi, he suggested I could alternatively have static zend_always_inline, non-ZEND_API functions the engine uses, then in the zend_atomic.c file have non-static, non-inline functions which delegate to the static inline ones. That didn't occur to me when writing the PR, but I prefer extern inline anyway because then there aren't two different names for each function and much less boilerplate overall. What do you think?

@morrisonlevi morrisonlevi force-pushed the atomic-vm-interrupt branch from f773fcf to 16dd40a Compare May 20, 2022 19:46
This is done by adding a new zend_atomic_bool type. The type
definition is only available for compiler alignment and size info; it
should be treated as opaque and only the zend_atomic_bool_* family of
functions should be used.

Note that directly using atomic_bool is complicated. All C++ compilers
stdlibs that I checked typedef atomic_bool to std::atomic<bool>, which
can't be used in an extern "C" section, and there's at least one usage
of this in core, and probably more outside of it.

So, instead use platform specific functions, preferring compiler
intrinsics.
I think the code is worse for doing this, but it was requested during
code review and I don't care to die on this hill.
@morrisonlevi morrisonlevi force-pushed the atomic-vm-interrupt branch from 16dd40a to e752c79 Compare May 20, 2022 19:55
@morrisonlevi
Copy link
Contributor Author

I've made the change to remove extern inline. Now there is zend_atomic_bool_* and zend_atomic_bool_*_ex where the former just delegates to the latter and the engine uses _ex everywhere. I believe this change actually makes this PR worse, however, I don't care to die on this hill today.

@ramsey
Copy link
Member

ramsey commented May 23, 2022

Needs @nikic to review and unblock

morrisonlevi and others added 2 commits May 25, 2022 10:41
twose pointed out that zend_portability.h already defines the
__has_feature no-op.
Copy link
Member

@bwoebi bwoebi left a comment

Choose a reason for hiding this comment

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

LGTM

@morrisonlevi
Copy link
Contributor Author

For whatever reason Nikita hasn't responded to messages here or in other places. I removed the extern inline that he complained about, so I'm going ahead with the merge.

@morrisonlevi morrisonlevi merged commit 280fd68 into php:master Jun 1, 2022
@morrisonlevi morrisonlevi deleted the atomic-vm-interrupt branch June 1, 2022 15:43
@twose
Copy link
Member

twose commented Jun 13, 2022

A feature I was developing just needed to use atomic, so I re-read this PR today, I found that there are still some things that can be improved, so I opened #8764.

And, to be honest, I do not like such a design that both have ZEND_API and inline functions.

I want there to be ZEND_API functions here which are callable from places such as extensions written in Rust.

IMO, providing atomic APIs is not Zend's job, if you want to support set EG(vm_interrupt) or EG(timed_out) in Rust, I recommend to add specified APIs like zend_vm_imterrupt() and zend_timed_out().

There are so many macros and inline functions in Zend that you can't require everything to be API...

Please correct me if I am wrong :)

@tstarling
Copy link
Contributor

[deleted wrong comment]

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.

8 participants