Skip to content
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

Use epoll_create() as a fallback for epoll_create1() #1590

Merged
merged 6 commits into from Jul 17, 2022

Conversation

link2xt
Copy link
Contributor

@link2xt link2xt commented Jul 2, 2022

Fixes #1473

Copy link
Collaborator

@Thomasdezeeuw Thomasdezeeuw left a comment

Choose a reason for hiding this comment

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

@link2xt as I've said it can't impact the performance of existing code, which this does. This will not be accepted as is.

@link2xt
Copy link
Contributor Author

link2xt commented Jul 3, 2022

@Thomasdezeeuw How does this impact the performance? Is epoll_create such a frequent operation that not calling fcntl saves any cycles? The crate doesn't have any benchmark, how do you measure it?

@Thomasdezeeuw
Copy link
Collaborator

@Thomasdezeeuw How does this impact the performance? Is epoll_create such a frequent operation that not calling fcntl saves any cycles? The crate doesn't have any benchmark, how do you measure it?

I'm not willing to revert to epoll_create on all platforms, I've stated this before.

@link2xt
Copy link
Contributor Author

link2xt commented Jul 3, 2022

Threre is zero difference in syscall implementation, both call do_epoll_create. epoll_create1 is not faster than epoll_create:
https://elixir.bootlin.com/linux/v5.19-rc4/source/fs/eventpoll.c#L2010

The only difference is that there is an additional fcntl syscall to set a flag once at the beginning of event loop.

@Thomasdezeeuw
Copy link
Collaborator

Threre is zero difference in syscall implementation, both call do_epoll_create. epoll_create1 is not faster than epoll_create: https://elixir.bootlin.com/linux/v5.19-rc4/source/fs/eventpoll.c#L2010

The only difference is that there is an additional fcntl syscall to set a flag once at the beginning of event loop.

Can I just say that this is becoming really tiring...

They reason that epoll_create1 was introduced is the same reason we use it. We don't control the entire program, so it's possible to fork between epoll_create and the next call to fcntl, causing the file descriptor to be leaked. We don't want this so we use epoll_create1.

@notgull
Copy link

notgull commented Jul 3, 2022

Is there any particular reason we can't use what the polling crate does for epoll, where it tries to use a syscall to call epoll_create1, and if that fails with ENOSYS, it falls back onto the epoll_create implementation? That would be the best of both worlds.

@link2xt
Copy link
Contributor Author

link2xt commented Jul 3, 2022

Is there any particular reason we can't use what the polling crate does for epoll, where it tries to use a syscall to call epoll_create1, and if that fails with ENOSYS, it falls back onto the epoll_create implementation? That would be the best of both worlds.

Sounds good, and should not degrade performance at all thanks to branch prediction: on systems with epoll_create1 it will always succeed and on older systems we don't care.

@Noah-Kennedy
Copy link

I think that would be fair to do. It shouldn't degrade performance or worsen anything for system which support the newer API, and would provide support for the older systems.

@Thomasdezeeuw
Copy link
Collaborator

That should fine with the addition of some documentation that we fall back to a racy version using epoll_create.

@link2xt
Copy link
Contributor Author

link2xt commented Jul 4, 2022

Note that kqueue selector is also racy. AFAIK only NetBSD offers kqueue1 that allows to set CLOEXEC atomically.

@Thomasdezeeuw
Copy link
Collaborator

Note that kqueue selector is also racy. AFAIK only NetBSD offers kqueue1 that allows to set CLOEXEC atomically.

True, but for kqueue we can't do better (except for NetBSD, but that's not a tier1 platform for Mio).

@link2xt link2xt changed the title Use epoll_create() instead of epoll_create1() Use epoll_create() as a fallback for epoll_create1() Jul 9, 2022
@link2xt
Copy link
Contributor Author

link2xt commented Jul 9, 2022

I have updated the PR to use epoll_create1 when it is available.

@notgull
Copy link

notgull commented Jul 9, 2022

Hmm, I don't think this works. I think you need to use the libc::syscall function to call epoll_create1. Otherwise, it'll try to link to the epoll_create1 function in libc, which may not exist.

#[cfg(debug_assertions)]
has_waker: AtomicBool::new(false),
})
syscall!(syscall(libc::SYS_epoll_create1, flag))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is probably even faster, because we no longer use epoll_create1 C function, but epoll_create1 syscall.

@link2xt
Copy link
Contributor Author

link2xt commented Jul 9, 2022

Fails on Illumos currently, because they have epoll_create1 function, but no syscall: https://github.com/illumos/illumos-gate/blob/29ed14768e5e76bb8c95f0a28b4d21d8741dfabe/usr/src/lib/libc/port/sys/epoll.c#L104

@link2xt
Copy link
Contributor Author

link2xt commented Jul 9, 2022

Hmm, I don't think this works. I think you need to use the libc::syscall function to call epoll_create1. Otherwise, it'll try to link to the epoll_create1 function in libc, which may not exist.

It's fixed now.

Copy link
Collaborator

@Thomasdezeeuw Thomasdezeeuw left a comment

Choose a reason for hiding this comment

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

This is going into the right direction. However we're not going to use raw system calls. Also please add the the documentation to Poll::new to I asked for regarding the possible data race.


// Try epoll_create1 syscall with an epoll_create fallback.
#[cfg(not(any(target_os = "illumos", target_os = "redox")))]
let ep = syscall!(syscall(libc::SYS_epoll_create1, flag))
Copy link
Collaborator

Choose a reason for hiding this comment

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

We're not going to use raw system calls. Please add the epoll_create1 function the libc crate instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem it started with is that Android API level 16 does not have epoll_create1 library function in the libc, so the linker fails if we call epoll_create1 libc function. The solution is to try calling raw system call epoll_create1 and use a fallback if it falls with ENOSYS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In my case the linker is not even called by cargo, because cargo is only used to build static .a library, so there is no way to detect whether libc has an epoll_create function from within the compiler.

Is there any problem with using a raw system call?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there any problem with using a raw system call?

It makes maintenance harder.

But I guess we can make an exception here, could you extract the system call stuff to a new epoll_create1 function, that for platforms that support it call libc::epoll_create1 and for others use the raw system interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did it, made a new_epoll_fd() function.

Copy link
Collaborator

@Thomasdezeeuw Thomasdezeeuw left a comment

Choose a reason for hiding this comment

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

Almost their I think, I'm still not super happy about using raw system calls, but if we can minimise the usage to just Android I think it would be acceptable.

let ep = syscall!(epoll_create1(flag))?;

// Try epoll_create1 syscall with an epoll_create fallback.
#[cfg(not(any(target_os = "illumos", target_os = "redox")))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you change this to be android only with a comment referring to #1473 and explaining that Android API level 16 doesn't define epoll_create1. I really like to minimise the use of raw system calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pushed a commit limiting this code to target_os = "android".

@Thomasdezeeuw
Copy link
Collaborator

I'm going to merge this as and will do a little cleanup and more docs into another pr.

@Thomasdezeeuw Thomasdezeeuw merged commit 8a96a4f into tokio-rs:master Jul 17, 2022
@link2xt link2xt deleted the no-epoll_create1 branch October 26, 2022 16:44
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.

error: undefined reference to 'epoll_create1'
4 participants