diff options
author | Eric Wong <bofh@yhbt.net> | 2023-03-28 12:24:37 +0000 |
---|---|---|
committer | Eric Wong <bofh@yhbt.net> | 2023-06-05 09:17:18 +0000 |
commit | 5299c3f255dada8605c2cffed9eba1b68d9d42b4 (patch) | |
tree | 0db877167498183bfdb9adf20ea395ec99f13889 /lib/unicorn | |
parent | 320bd05941374339c3a493dabde3e692ffa2881e (diff) | |
download | unicorn-5299c3f255dada8605c2cffed9eba1b68d9d42b4.tar.gz |
This allows us to avoid both malloc (slow) and alloca (unpredictable stack usage) at the cost of needing to make more epoll_wait syscalls in a rare case. In unicorn (and most servers), I expect the most frequent setup is to have one active listener serving the majority of the connections, so the most frequent epoll_wait return value would be 1. Even with >1 events, any syscall overhead saved by having epoll_wait retrieve multiple events is dwarfed by Rack app processing overhead. Worse yet, if a worker retrieves an event sooner than it can process it, the kernel (regardless of EPOLLEXCLUSIVE or not) is able to enqueue another new event to that worker. In this example where `a' and `b' are both listeners: U=userspace, K=kernel K: client hits `a' and `b', enqueues them both (events #1 and #2) U: epoll_wait(maxevents: 2) => [ a, b ] K: enqueues another event for `b' (event #3) U: process_client(a.accept) # this takes a long time While process_client(a.accept) is happening, `b' can have two clients pending on a given worker. It's actually better to leave the first `b' event unretrieved so the second `b' event can go to the ep->rdllist of another worker. The kernel is only capable of enqueuing an item if it hasn't been enqueued. Meaning, it's impossible for epoll_wait to ever retrieve `[ b, b ]' in one call.
Diffstat (limited to 'lib/unicorn')
0 files changed, 0 insertions, 0 deletions