Skip to content

Omnibus to implement CFRunLoop with kevent. #3004

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
Aug 2, 2022

Conversation

3405691582
Copy link
Member

@3405691582 3405691582 commented Jul 9, 2021

Dispatch uses kevent for its event loop backend. However, CFRunLoop in Foundation uses Mach ports. This means for other kevent-based platforms such as OpenBSD, new runloop functionality is required, which this omnibus pr implements.

This pr must be considered in concert with swiftlang/swift-corelibs-libdispatch#559 (see below).

Please feel free to request any or all to be split into multiple prs, though all these commits and the aforementioned Dispatch pr are required to properly implement CFRunLoop on this platform.

Notes:

  • Some of the Mach-based implementation refers to MACH_PORT_NULL, for example, when we have symbols already defined like CFPORT_NULL. These are brought more into alignment for consistency.
  • Each platform necessitates implementing the "backend" set of __CFPort* and mk_timer_* stubs. It does not make sense if these methods are not implemented for a given platform, so add a default #error case. (The code would greatly benefit from reorganizing the necessary stubs to implement together in one #if conditional, but this should probably be done separate from this omnibus as it is not critically important to achieve our result. However, we have implemented all the stubs together below in this commit.)
  • A tentative kevent-based implementation of these stubs are provided. In Support OpenBSD. swift-corelibs-libdispatch#559, we introduced a runloop implementation using pipe2 and packing each 32-bit fd in the pipe into a 64-bt integer. Since CFRunLoop fraternizes with Dispatch via _dispatch_main_queue_callback_4CF, this means that dispatch_runloop_handle_t in Dispatch must match __CFPort here. Unfortunately, this means that we have to perform some bit-packing shenanigans to handle kevent timers. There might be nicer ways to do this, but we'll defer that for now.
  • We need to fix SR-14288 while we are here to ensure the runloop timers work correctly and the tests pass properly.
  • While implementing the runloop, testing has uncovered some strange behavior with pthread_main_np and TSD on this platform. Work around this by doing a simple pthread_self comparison.

@3405691582
Copy link
Member Author

cc @millenomi -- input or advice on how to proceed?

@AZero13
Copy link
Contributor

AZero13 commented Oct 4, 2021

@compnerd any thoughts?

@finagolfin
Copy link
Member

@parkera, mind reviewing?

@finagolfin
Copy link
Member

@phausler, maybe you could review?

CFRunLoop has a recurring abstraction for platform-specific code, but it
is a little leaky. Plug these leaks: ensure `MACH_PORT_NULL` in the
generic, non-platform context is rewritten as `CFPORT_NULL`, and ensure
that `kern_return_t` and `KERN_SUCCESS` are defined properly.
New platforms must define these types and functions; it is incorrect to
not have #else cases here.
@finagolfin
Copy link
Member

@etcwilde, would you review?

Copy link
Contributor

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

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

This looks fine for the most part. I left a couple comments on style.

I'm unfortunately not familiar enough with this part of the codebase to really identify logic/behavior issues in the implementation of the __CFPort* or mk_timer function implementations down in CFRunLoop.c. It looks fine from what I can tell, but it would be better to get someone more familiar with this code to look over it and verify that it's correct.

As I'm not very familiar with this part of the codebase, nor am I an owner or maintainer of foundation, I don't really feel comfortable just approving it. I'll leave that part up to @parkera.

static inline uint64_t __CFNanosecondsToTSR(uint64_t ns) {
#if TARGET_OS_MAC || TARGET_OS_LINUX
return ns;
#else
Copy link
Contributor

Choose a reason for hiding this comment

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

@compnerd, is this the appropriate implementation of this function for Windows?

@@ -249,7 +249,11 @@ const char *_CFProcessPath(void) {

#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_BSD
CF_CROSS_PLATFORM_EXPORT Boolean _CFIsMainThread(void) {
#if !defined(__OpenBSD__)
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a bit clearer if we have the OpenBSD case come first and not invert the condition.

#if defined(__OpenBSD__) // special case for OpenBSD
    return pthread_equal(pthread_self(), _CFMainPThread) != 0;
#else // everything else
    return pthread_main_np() == 1;
#endif

I usually think of else indicating anything not special-cased, so having the else only catch the OpenBSD case seems a bit odd.
Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

Reordered the conditional.

EV_SET(
&tev,
id,
EVFILT_TIMER,
Copy link
Contributor

Choose a reason for hiding this comment

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

Inconsistent indentation through here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

During runloop testing, log messages suggest that the runloop thinks the
main thread has exited and certain runloop operations don't actually
proceed. The flag for denoting when the main thread has exited is set in
the thread-specific data destructor `__CFTSDFinalize`.

This function detects whether the thread in question is the main thread
via `pthread_main_np`, and has been observed in debugging this problem
to return 1 on apparently non-main threads on OpenBSD. This obviously
will cause the main thread exited flag to be erroneously set when
non-main threads complete their work and dispose of thread-specific
data.

Instead, use the existing `_CFMainPThread` symbol set in
`__CFInitialize` to determine the main thread. I am not sure whether the
platform `pthread_main_np` is at fault here, but since this workaround
makes the tests pass, let's use it.
Copy link
Contributor

@parkera parkera left a comment

Choose a reason for hiding this comment

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

Looks good to me.

@parkera
Copy link
Contributor

parkera commented Aug 1, 2022

@swift-ci test

@compnerd
Copy link
Member

compnerd commented Aug 9, 2022

I think that this might be causing the timeout on Windows ...

compnerd added a commit that referenced this pull request Aug 9, 2022
3405691582 added a commit to 3405691582/swift-corelibs-foundation that referenced this pull request Oct 21, 2022
* [runloop] Set up some symbols for generic code.

CFRunLoop has a recurring abstraction for platform-specific code, but it
is a little leaky. Plug these leaks: ensure `MACH_PORT_NULL` in the
generic, non-platform context is rewritten as `CFPORT_NULL`, and ensure
that `kern_return_t` and `KERN_SUCCESS` are defined properly.

* [runloop] Remind porter these stubs are required.

New platforms must define these types and functions; it is incorrect to
not have #else cases here.

* [runloop] Implement runloop abstraction for BSD.

* [runloop] Avoid pthread_main_np on OpenBSD.

During runloop testing, log messages suggest that the runloop thinks the
main thread has exited and certain runloop operations don't actually
proceed. The flag for denoting when the main thread has exited is set in
the thread-specific data destructor `__CFTSDFinalize`.

This function detects whether the thread in question is the main thread
via `pthread_main_np`, and has been observed in debugging this problem
to return 1 on apparently non-main threads on OpenBSD. This obviously
will cause the main thread exited flag to be erroneously set when
non-main threads complete their work and dispose of thread-specific
data.

Instead, use the existing `_CFMainPThread` symbol set in
`__CFInitialize` to determine the main thread. I am not sure whether the
platform `pthread_main_np` is at fault here, but since this workaround
makes the tests pass, let's use it.
eleanxr pushed a commit to eleanxr/ravynos that referenced this pull request Nov 11, 2022
Omnibus to implement CFRunLoop with kevent.

[runloop] Set up some symbols for generic code.

CFRunLoop has a recurring abstraction for platform-specific code, but it
is a little leaky. Plug these leaks: ensure `MACH_PORT_NULL` in the
generic, non-platform context is rewritten as `CFPORT_NULL`, and ensure
that `kern_return_t` and `KERN_SUCCESS` are defined properly.

[runloop] Remind porter these stubs are required.

New platforms must define these types and functions; it is incorrect to
not have #else cases here.

[runloop] Implement runloop abstraction for BSD.

[runloop] Ensure timing arithmetic is done in ns.

Fixes SR-14288.

[runloop] Avoid pthread_main_np on OpenBSD.

During runloop testing, log messages suggest that the runloop thinks the
main thread has exited and certain runloop operations don't actually
proceed. The flag for denoting when the main thread has exited is set in
the thread-specific data destructor `__CFTSDFinalize`.

This function detects whether the thread in question is the main thread
via `pthread_main_np`, and has been observed in debugging this problem
to return 1 on apparently non-main threads on OpenBSD. This obviously
will cause the main thread exited flag to be erroneously set when
non-main threads complete their work and dispose of thread-specific
data.

Instead, use the existing `_CFMainPThread` symbol set in
`__CFInitialize` to determine the main thread. I am not sure whether the
platform `pthread_main_np` is at fault here, but since this workaround
makes the tests pass, let's use it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants