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
[SR-14920] CFRunLoop reads 8 bytes from each file descriptor it listens on Linux #3953
Comments
cc @millenomi ? (Not sure who the right person to look at this is) |
@swift-ci create |
Really tempted to just outright say that if you're not XCTest, you should really not use CFRunLoop (and you already can't outside of Linux). |
Comment by Serhii Mumriak (JIRA) hello @millenomi, The even more interesting parts come further: majority (if not all, can't say for sure) libraries that do any I/O expose the file descriptor that is not always eventfd (some are pipes, some are timers, some are sockets, etc). Particularly, UI toolkits like GTK/GDK, Qt, weston (as reference wayland compositor) and others. I also understand (and really very welcome) the async coroutines direction of swift and do realize that anyone can literally observe file descriptors via DispatchSource on background queue. Tho for some tasks, especially UI handling, this might not be the most efficient way to do things. Particularly, in Xlib (and xcb for that matter) it's very discouraged to have multithread processing of events and sending requests to X11 server. And doing some thing asynchronously (like reacting to map event or expose event) might (and will) introduce visible jitter and lag. That said, Chriss Lattner in his swift concurrency manifesto suggest having ability to opt-out from the async nature of code for system libraries or frameworks and this is exactly this case for me. (That's a huge wall of text, thanks for reading to this point!) With all that said, there is some work that I've started doing:
Regarding other platforms. In my specific case I am looking into expanding the UI framework to windows, where concept of "handles" is very similar to file descriptor. CFRunLoop already correctly works with those (as far as i can tell from my limited knowledge for win32 API), especially with regard of calling `ResetEvent` only for wakeUpPort handle. What do you think about that? I am really looking for feedback 🙂 PS. Does it feel that I just like RunLoop too much and it's an outdated technology now? I am so used to RunLoop as a thing on Darwing platforms |
Comment by Serhii Mumriak (JIRA) Omg, I'm so dumb. I don't want CFMachPort equivalent, I want this: https://developer.apple.com/documentation/corefoundation/cffiledescriptor?language=objc You can re-enable the callback in the callback function itself, but you must completely service the file descriptor before doing so. |
Comment by Serhii Mumriak (JIRA) Hey @millenomi I've ported CFFileDescriptor on Linux and implemented a swift wrapper for that in Foundation (actually spent like ~2 weeks on that without knowing about CFFileDescriptor on Darwin). Right now I have only three tests for that thing, but it requires much more unit testing to be added. Especially considering that there's a bug in kernel that prevents nested epoll fd's to be monitored in edge-triggered mode. Because of that the "one shot" behavior can not be implemented, so i had to do a workaround. I'm having a lot of fun with that 🙂 |
PALKOVNIK (JIRA User) Could you perhaps share your CFFileDescriptor implementation? Due to severe bugs in NSFileHandle.read, I‘m afraid I need to implement reading myself and CFFileDescriptor would be very important since I also have a NSRunloop to integrate with. |
Comment by Serhii Mumriak (JIRA) Hello @mickeyl, |
Attachment: Download
Environment
Any swift version. I've noticed the issue in swift 5.1 or something like that, but based on the code it was present since the original support of Linux's epoll was introduced in CFRunLoop.
Additional Detail from JIRA
md5: 5d0bc6cdfdf95adc92e299ec86b5ecdc
Issue Description:
CFRunLoop on Linux is using epoll on order to wait for events on "ports", which are just file descriptors on Linux. This includes the port used to wake up run loop. In order to tell run loop that it's awoken the code writes value of `1` as 8 byte integer into the wake up file descriptor and to "reset" the file descriptor state the code reads same 8 byte integer from it. The issue is in the fact that client code can register custom CFRunLoopSource with run loop and the code in run loop itself would read the same 8 bytes from the underlying file descriptor as if it would the wake up file descriptor.
I've prepared a very small sample project to reproduce the issue (it's a tiny part of my pet project) that can be found in attached archive. Jut do `swift run` on any Linux machine with `libx11-dev` installed and click few buttons on the keyboard in the window created by the app. xlib will abort execution saying
```
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
```
xlib is capable to understand that there's incorrect amount of data read from it's connection file descriptor and abort execution
The text was updated successfully, but these errors were encountered: