From http://www.erlang.org/doc/man/gen_tcp.html#accept-1:
It is worth noting that the accept call does not have to be issued from the socket owner proce
I also posted this question in the Erlang Questions mailing list. As pointed out by Daniel Goertzen, there are acceptor pool libraries in Erlang, such as ranch and swarm.
ranch works differently from Unicorn in such a way that it only does "accept" in many processes, and then passes the socket to some worker process.
The way swarm works is the same as Unicorn in the sense that the acceptor and worker is combined. (Thanks to Loïc Hoguin for pointing out) But they are a bit different because swarm can accept a new socket in parallel with the processing of the accepted socket, while Unicorn only accepts after the accepted socket is processed
I prefer the swarm style since it's ideal for both fast and slow requests, while Unicorn requires fast requests.
Instead of attempting to be efficient at serving slow clients, unicorn relies on a buffering reverse proxy to efficiently deal with slow clients.
unicorn is not suited for all applications. unicorn is optimized for applications that are CPU/memory/disk intensive and spend little time waiting on external resources (e.g. a database server or external API).
unicorn is highly inefficient for Comet/reverse-HTTP/push applications where the HTTP connection spends a large amount of time idle.