Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 10 Aug 2000 19:52:52 -0500
From:      Michael Owens <owensmk@earthlink.net>
To:        hackers@freebsd.org
Subject:   R. Stevens select() Collisions Scenario
Message-ID:  <39934E64.A5BEE8EE@earthlink.net>

next in thread | raw e-mail | index | archive | help
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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?39934E64.A5BEE8EE>