grady@fxgrp.fx.com (Steven Grady) (12/16/89)
do_select(), the function that implements UNIX select(), does not work properly. There are two problems. First, it uses the length of the vector (represented as a string internally) as the width of the bit field passed to select(). Unfortunately, it is including the '\0' terminator of the string as part of the length. With the width off by 8, the file descriptors examined are 8 higher than they should be. Second and more important, it does not work on suns. It does not use the FD_* macros. The representation it uses for the file descriptor bits, namely, a simple string, does not work at all (because the width on a sun must be a multiple of 32 bits). Here is a patch to fix do_select(). It is not very pretty, but it gets the job done. There is no compile flag to enable it, since it is not a complete patch anyway. This version will work on suns, but it won't work, for instance, on AIX (which uses "sellist" structures). The "correct" way to do things would be to avoid explicitly using perl vecs. I'd like to see a different interface to select() anyway. Perhaps something like: (@readready, @writeready, @exceptready, $timeleft) = select(@readhandles, @writehandles, @excepthandles, $timeout); where each of the arrays would be an array of expressions evaluating to file handles. The vec interface is pretty bogus (and doesn't work, under the current implementation). *** doio.c Fri Dec 15 20:26:16 1989 --- doio.c.BAK Fri Dec 15 20:07:44 1989 *************** *** 1510,1537 **** else tbuf = Null(struct timeval*); ! { ! fd_set fds[4]; ! register int i, j; ! for (j = 1; j <= 3; j++) { ! FD_ZERO(&fds[j]); ! if (st[sp+j]->str_ptr == NULL) continue; ! for (i = 0; i < (maxlen - 1) * 8; i++) { ! if (st[sp+j]->str_ptr[i/8] & (1 << (i % 8))) ! FD_SET(i, &fds[j]); ! } ! } ! nfound = select( ! (maxlen - 1) * 8, &fds[1], &fds[2], &fds[3], tbuf); ! for (j = 1; j <= 3; j++) { ! if (st[sp+j]->str_ptr == NULL) continue; ! bzero(st[sp+j]->str_ptr, (maxlen - 1)); ! for (i = 0; i < (maxlen - 1) * 8; i++) { ! if (FD_ISSET(i, &fds[j])) ! st[sp+j]->str_ptr[i/8] |= (1 << (i % 8)); ! } ! } ! } st[++sp] = str_static(&str_no); str_numset(st[sp], (double)nfound); --- 1510,1521 ---- else tbuf = Null(struct timeval*); ! nfound = select( ! maxlen * 8, ! st[sp+1]->str_ptr, ! st[sp+2]->str_ptr, ! st[sp+3]->str_ptr, ! tbuf); st[++sp] = str_static(&str_no); str_numset(st[sp], (double)nfound); -- Steven ...!ucbvax!grady grady@postgres.berkeley.edu Spock was waiting for them when they got to the conference room. "Captain, I've run the data we collected through the computer." "Well, Spock, you must be a very proud young man. So what's the deal with these council weasels?"
lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (12/19/89)
In article <1989Dec16.044106.7924@fxgrp.fx.com> grady@fxgrp.fx.com (Steven Grady) writes: : do_select(), the function that implements UNIX select(), does not work : properly. There are two problems. First, it uses the length of the : vector (represented as a string internally) as the width of the bit : field passed to select(). Unfortunately, it is including the '\0' : terminator of the string as part of the length. With the width off by : 8, the file descriptors examined are 8 higher than they should be. This is slightly misleading. The problem is that it was basing the length of the vector on the allocated length of the string rather than the current length of the string. There's nothing to prevent the allocated length from being any old number greater than the current length. The correct patch for this is not to subtract 8 bits from the length, but to base the length on str_cur rather than str_len. : Second and more important, it does not work on suns. It does not use : the FD_* macros. The representation it uses for the file descriptor : bits, namely, a simple string, does not work at all (because the width : on a sun must be a multiple of 32 bits). Yes, it's basically a byte reordering problem. : Here is a patch to fix do_select(). It is not very pretty, but it gets : the job done. There is no compile flag to enable it, since it is not a : complete patch anyway. This version will work on suns, but it won't : work, for instance, on AIX (which uses "sellist" structures). I've got a do_select for patch 7 that should work everwhere that uses bitmaps composed of ints or longs. Even places that don't have FD_SET(), etc. (if there are any such). Someone will have to tell me about "sellist" structures, though. Patch 7 should be out later this week. : The : "correct" way to do things would be to avoid explicitly using perl : vecs. I'd like to see a different interface to select() anyway. : Perhaps something like: : : (@readready, @writeready, @exceptready, $timeleft) = : select(@readhandles, @writehandles, @excepthandles, $timeout); : : where each of the arrays would be an array of expressions evaluating to : file handles. The vec interface is pretty bogus (and doesn't work, : under the current implementation). I respectfully disagree. The proposed interface would be very difficult to make efficient. Several net.people will attest that I thought long and hard about the interface to select (pun not intended). I considered and rejected quite a few interfaces. On top of which, your arguments are inconsistent with the way LISTs work. And the assignment to multiple arrays would cause all the array values to be assigned to the first array, or I'd have to special case assignments that come from select, yech. And I also disagree that vec() interface is "pretty bogus". I basically get to still two kurds with one bone: first, I get a representation of an fd set that is easy to pass to the system call select() without much processing; second, I get a general facility for doing bitmaps, which I want for other reasons (like article sets in a newsreader :-). If you want to do it the "correct" but slow way, here's a subroutine that has approximately the interface you desire. It differs in that the ready filehandles are returned in the associative array of the same name. ;# Usage: ;# @readmap = ('STDIN','FOO','BAR'); ;# @writemap = ('STDOUT','STDERR'); ;# @exceptmap = (@readmap,@writemap); ;# ;# $nfound = &select(*readmap,*writemap,*exceptmap,$timeout); ;# or ;# ($nfound,$timeleft) = &select(*readmap,*writemap,*exceptmap,$timeout); ;# ;# if ($nfound) { ;# if ($readmap{'stdin'}) { sub select { local(*r,*w,*e,$timeout) = @_; local($nfound,$rmap,$wmap,$emap); foreach $handle (@r) { vec($rmap,fileno($handle),1) = 1; } foreach $handle (@w) { vec($wmap,fileno($handle),1) = 1; } foreach $handle (@e) { vec($emap,fileno($handle),1) = 1; } ($nfound,$timeout) = select($rmap,$wmap,$emap,$timeout); %r = (); %w = (); %e = (); foreach $handle (@r) { $r{$handle} = 1 if vec($rmap,fileno($handle),1); } foreach $handle (@w) { $w{$handle} = 1 if vec($wmap,fileno($handle),1); } foreach $handle (@e) { $e{$handle} = 1 if vec($emap,fileno($handle),1); } if (wantarray) { return $nfound,$timeout; } else { return $nfound; } } Or some such. I haven't tested it. By the by, I don't know of any systems that DO implement $timeleft yet. Larry
mdb@kosciusko.esd.3com.com (Mark D. Baushke) (12/20/89)
On 19 Dec 89 07:04:01 GMT, lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) said: Larry> I've got a do_select for patch 7 that should work everwhere Larry> that uses bitmaps composed of ints or longs. Even places that Larry> don't have FD_SET(), etc. (if there are any such). There are indeed machines without FD_SET(). For example, SunOS 3.x does NOT have it. I believe that at least some versions Ultrix (e.g., 2.0) also do not have it. I have seen something like the following code used on SunOS 3.x and Ultrix 2.0 machines as a workaround. /* * Ultrix 2.0/SunOS 3.x File Descriptor Compatibility Macros * * BIG NOTE!!! This will only allow fd_sets of 32 bits (assuming that's * the size of an int). Also, SunOS 3.x only allows 30 FDs in any case. * */ #ifndef FD_SET #define NBBY 8 /* number of bits in a byte */ #define NFDBITS (sizeof(int) * NBBY) /* * This is for Ultrix 2.0 and SunOS 3.x where fd_sets only have 1 element. */ /* I'm assuming that the only use of howmany is howmany(FD_SETSIZE, NFDBITS), which should always return 1. The following code will need to be uncommented otherwise. #define howmany(x, y) (((x)+((y)-1))/(y)) */ #define howmany(x,y) 1 #ifndef FD_SETSIZE #define FD_SETSIZE NFDBITS #endif #define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) ((p)->fds_bits[0] = 0) #endif /* FD_SET */ Larry> Someone will have to tell me about "sellist" structures, Larry> though. Hope this helps, -- Mark D. Baushke mdb@ESD.3Com.COM