From owner-freebsd-hackers Thu Aug 10 17:49:55 2000 Delivered-To: freebsd-hackers@freebsd.org Received: from hawk.prod.itd.earthlink.net (hawk.prod.itd.earthlink.net [207.217.120.22]) by hub.freebsd.org (Postfix) with ESMTP id 9629937BB99 for ; Thu, 10 Aug 2000 17:49:51 -0700 (PDT) (envelope-from owensmk@earthlink.net) Received: from earthlink.net (sdn-ar-002txfworP026.dialsprint.net [168.191.159.138]) by hawk.prod.itd.earthlink.net (8.9.3-EL_1_3/8.9.3) with ESMTP id RAA23715 for ; Thu, 10 Aug 2000 17:49:49 -0700 (PDT) Message-ID: <39934E64.A5BEE8EE@earthlink.net> Date: Thu, 10 Aug 2000 19:52:52 -0500 From: Michael Owens X-Mailer: Mozilla 4.72 [en] (X11; U; Linux 2.2.14-5.0 i686) X-Accept-Language: en MIME-Version: 1.0 To: hackers@freebsd.org Subject: R. Stevens select() Collisions Scenario Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG Purpose: I am trying to write a non-blocking, preforked server, specifically to run on FreeBSD, and have a general question as to whether or not my strategy is sound. Problem: In Unix Network Programming Vol. 1 (section 27.6, p.741), Stevens mentions a scenario under a preforked server design where multiple children are calling select() on the same descriptor, causing collisions for the kernel to resolve. The children would look something like... . . . for( ; ; ){ FD_SET(listfd,&rset); Select(listfd+1,&rset,NULL,NULL,NULL); if(FD_ISSET(listfd,&rset)==0) err_quit("listenfd readable"); clilen = addrlen; connfd = Accept(listenfd, cliaddr, &clilen); process(connfd); Close(connfd); } . . . Hypothesis: If you were implement the server such that the children used non-blocking I/O, so that select() returned immediately, would this alleviate the problem of collisions in the kernel and its associated overhead? If so, would you then have to worry about mutual exclusion when select did return a ready descriptor for accept(). For example, a child gets switched just after calling select() but just before it makes it to accept(). Thus, the next child would also receive the same descriptor ready for accept()? Now only one of the two children will get to accept() first, leaving the other blocking on accept(). Stevens mentions a similar case at the end of the Nonblocking I/O chapter (section 15.6, p. 422) and to avoid this he recommends 1) setting the listening descriptor to non-blocking and 2) ignoring EWOULDBLOCK, ECONNABORTED, etc. on accept(). So, if you make the listening descriptor non-blocking, and treat select() and accept() appropriately, you should be alright in both avoiding select collisions and there overhead, as well as avoid children blocking due to losing possible race conditions for the listen descriptor in the event (albiet small) of a context switch between select() and accept(). Apology: The reason I bother you with all this is that while I (think I) understand the logic, I (know I) am at the limits of my understanding, and might be missing some important considerations as to other goings on in the kernel, and that there might be a better way to go about all this. Summary: Ultimately, all I am seeking to do is have an efficient and scalable server design, and to my knowledge, this would consist of a number of preforked children who are non-blocking/multiplexing themselves. Furthermore, each child will have a pool of worker threads which will handle jobs sent by different clients. As long as the threads' work doesn't entail an operation that blocks for appreciable amount of time (so that a single thread puts the whole child to sleep), it would seem like this might be a decent proposal: multiple clients and multiple jobs being handled simultaneously by each child, and the number of children can be controlled by the parent according to the load and limits of the hardware. Does this seem like a good way to go about it? Thanks: Thanks. To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message