Announcement

Collapse
No announcement yet.

Linux Changes Pipe Behavior After Breaking Problematic Android Apps On Recent Kernels

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • F.Ultra
    replied
    Originally posted by indepe View Post

    It sounds like you misunderstood me. The receipt goes in the opposite direction, back to the sender of the update message. Or maybe I don't understand your point.
    ah, that makes more sense, thought you talked about messages in one direction only here

    Leave a comment:


  • indepe
    replied
    Originally posted by F.Ultra View Post
    But what happens if due to timing issues (your reading thread gets scheduled between epoll_wait and read due to paging/whatever) so the last receipt+updates gets merged into a single message, then you only read part of the buffer and only discover the receipt but not the updates?! Unless of course you do some checking on timeout.
    It sounds like you misunderstood me. The receipt goes in the opposite direction, back to the sender of the update message. Or maybe I don't understand your point.

    Leave a comment:


  • F.Ultra
    replied
    Originally posted by indepe View Post
    Actually no, since without a receipt, there will be no additional update message (that could be merged). This is to avoid a flood of update messages at times when they can't be processed. I could imagine a similar mechanism to guarantee the ability to re-send a message even in situations when the connection needs to be re-established.
    But what happens if due to timing issues (your reading thread gets scheduled between epoll_wait and read due to paging/whatever) so the last receipt+updates gets merged into a single message, then you only read part of the buffer and only discover the receipt but not the updates?! Unless of course you do some checking on timeout.

    Leave a comment:


  • indepe
    replied
    Originally posted by F.Ultra View Post
    Level-trigger will return from epoll_wait as long as there is data to read, aka as long as the kernel buffer has data the level is set so the difference would be that in the case where you don't empty the buffer until EAGAIN edge would not provide further events while level would return immediately.
    In the intermediate version, and in that scenario, reading until EAGAIN is required to avoid infinite blocking. And it seems that if doing that, then edge-triggered and level-triggered will behave the same. (Except for a multi-threaded scenario like I described above, which however would work *only* with the intermediate version.)

    Originally posted by F.Ultra View Post
    I guess it's multiple processes and written to be run on systems without named semaphores?
    I'd think for example System V semaphores would be an alternative. I don't know if maybe some relevant system would not have inter-process semaphores at all, though I wouldn't expect that. But maybe pipes are considered better portable, and the intention was to avoid system-specific code for different semaphore APIs.

    Originally posted by F.Ultra View Post
    Now it sounds like you control both the read and the writer here, but would such a setup not make your application to end up in a situation where the processing never happens due to some of the write events being merged into a single one so you hang on epoll_wait?
    Actually no, since without a receipt, there will be no additional update message (that could be merged). This is to avoid a flood of update messages at times when they can't be processed. I could imagine a similar mechanism to guarantee the ability to re-send a message even in situations when the connection needs to be re-established.

    Leave a comment:


  • F.Ultra
    replied
    Originally posted by indepe View Post
    If you don't know such situations, it may be a bit difficult to imagine. I know such situations (generally speaking) where I send receipts immediately in order to keep the handshaking up-to-date, yet postpone processing the data until certain other conditions are met, and then batch-process it in one go. It probably just means that the fact that data has arrived, and the data itself, are considered two separate pieces of information that are handled separately.
    Now it sounds like you control both the read and the writer here, but would such a setup not make your application to end up in a situation where the processing never happens due to some of the write events being merged into a single one so you hang on epoll_wait? Or you process the data on timeout as well and don't much of a latency requirement?

    Leave a comment:


  • F.Ultra
    replied
    Originally posted by indepe View Post
    By the way, why doesn't the jobserver use a shared counting semaphore?
    I guess it's multiple processes and written to be run on systems without named semaphores?

    Leave a comment:


  • F.Ultra
    replied
    Originally posted by indepe View Post

    Additional response:

    However if edge-triggered epoll_wait would return only for a write on empty file, then how would edge-triggered be different from level-triggered?
    Is it just me, or did the previous version conflate the two?

    EDIT: Perhaps some use edge-triggered to restart epoll_wait immediately while another thread is reading the data, such that epoll_wait will return only once that other thread has finished reading, and then more data comes in.

    EDIT 2: But I guess that would have worked as intended only with the intermediate version.
    Level-trigger will return from epoll_wait as long as there is data to read, aka as long as the kernel buffer has data the level is set so the difference would be that in the case where you don't empty the buffer until EAGAIN edge would not provide further events while level would return immediately. This is of course more prominent for EPOLLOUT events since the level is almost always set (a write would be non-blocking) while an edge would be a seldom event (going from write would block to not block).

    Also accept() have a thundering herd problem in multithread with level trigger. And since you can only have one instance per fd in the epoll you might have to add it edge-triggered for reads even if you only really needed the edge-trigger mechanism for say writes.

    Leave a comment:


  • indepe
    replied
    By the way, why doesn't the jobserver use a shared counting semaphore?

    Leave a comment:


  • indepe
    replied
    Originally posted by F.Ultra View Post
    You are correct in that level trigger would return as long as there is data to be read and not only on each write, [...]
    Additional response:

    However if edge-triggered epoll_wait would return only for a write on empty file, then how would edge-triggered be different from level-triggered?
    Is it just me, or did the previous version conflate the two?

    EDIT: Perhaps some use edge-triggered to restart epoll_wait immediately while another thread is reading the data, such that epoll_wait will return only once that other thread has finished reading, and then more data comes in.

    EDIT 2: But I guess that would have worked as intended only with the intermediate version.
    Last edited by indepe; 04 August 2021, 11:38 PM.

    Leave a comment:


  • indepe
    replied
    Originally posted by F.Ultra View Post
    yes epoll_wait does not have to do anything more but I was more thinking about the code in the kernel that handles the "data comes in from socket/pipe/whatever" since it has to call some code internally to notify among other things epoll. Now this might just be it setting a flag and if so then there is no real gains to be made, but it could also be some more complicated code being called (I just don't know since I have not checked what the kernel does here).
    Right, I was just thinking about that. epoll_create will trigger some code that gets executed even in the absence of epoll_wait. But as you say, I would expect that to be a minor optimization since checking for a waiter could potentially be as easy as checking a flag. It's difficult to tell from the small diff in the commit, since one can't see the part of the code that it has an effect on.

    Originally posted by F.Ultra View Post
    Also you can use the edge-trigger case to signal you on which fd:s that have data, read only some of it and then call epoll_wait with a zero timeout if you have outstanding reads so that you give epoll the chance to give you more fd:s and at the same time being able to quickly read some more data from your list of fd:s with outstanding data. Now there is the ONESHOT flag for this but then you have to waste a syscall after each EAGAIN, which of course could be a drop in the bucket since you already have called both read and epoll_wait, but then again 3 syscalls are more than 2.
    That's where it gets complex...

    Originally posted by F.Ultra View Post
    You are correct in that level trigger would return as long as there is data to be read and not only on each write, it's just me that have a hard time understanding the real need to get notified every time there is a write done but at the same time not being interested in all the data said write wrote which means that you get to be notified that the writer wrote some more for you to then only read what that the writer actually wrote the last time (or parts of it, or it and parts of the new write).
    If you don't know such situations, it may be a bit difficult to imagine. I know such situations (generally speaking) where I send receipts immediately in order to keep the handshaking up-to-date, yet postpone processing the data until certain other conditions are met, and then batch-process it in one go. It probably just means that the fact that data has arrived, and the data itself, are considered two separate pieces of information that are handled separately.

    Leave a comment:

Working...
X