Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

FileDescriptor.cc

Go to the documentation of this file.
00001 /**************************************************************************
00002  Copyright (c) 2000-2001, Tony Garnock-Jones
00003  All rights reserved.
00004 
00005  Redistribution and use in source and binary forms, with or without
00006  modification, are permitted provided that the following conditions are
00007  met:
00008 
00009      * Redistributions of source code must retain the above copyright
00010        notice, this list of conditions and the following disclaimer.
00011 
00012      * Redistributions in binary form must reproduce the above
00013        copyright notice, this list of conditions and the following
00014        disclaimer in the documentation and/or other materials provided
00015        with the distribution.
00016 
00017      * Neither the names of the copyright holders nor the names of this
00018        software's contributors may be used to endorse or promote
00019        products derived from this software without specific prior
00020        written permission.
00021 
00022  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00025  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
00026  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00027  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00028  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00029  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00030  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00031  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 **************************************************************************/
00034 
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <stdio.h>
00038 
00039 #include <sys/types.h>
00040 #include <sys/time.h>
00041 #include <sys/stat.h>
00042 #include <fcntl.h>
00043 #include <unistd.h>
00044 #include <errno.h>
00045 #include <time.h>
00046 
00047 #include <FileDescriptor.h>
00048 
00049 #define WANT_FD_DEBUG_MESSAGES  0
00050 
00051 #ifndef MAX
00052 #define MAX(a, b) ((a > b) ? a : b)
00053 #endif
00054 
00055 #ifndef MIN
00056 #define MIN(a, b) ((a < b) ? a : b)
00057 #endif
00058 
00059 ///////////////////////////////////////////////////////////////////////////
00060 
00061 FileDescriptor::FileDescriptor(ref<FileDescriptorManager> const &_fdMgr, int _fd)
00062   : fdMgr(_fdMgr),
00063     fd(_fd),
00064     buflen(0),
00065     pos(0),
00066     readCallback(),
00067     writeCallback()
00068 {
00069   // Don't call updateCallbacks here. Wait until someone actually
00070   // *sets* a callback. This because: if we're registered with the
00071   // fdMgr while still being constructed, and our subclass'
00072   // constructor throws an exception, then when close() is called, the
00073   // fdMgr will be told to remove its reference to us. But since *at
00074   // that time* it's the only reference available, it will decrement
00075   // to zero, and call our destructor again, before the outer
00076   // invocation of the dtor has finished. Leading to much mess.
00077   //
00078   // This applies to subclass ctors too: don't call updateCallbacks()
00079   // or set{Read,Write}Callback() from them either - otherwise you
00080   // will end up with the same problem if an exception is thrown
00081   // during construction.
00082 #if WANT_FD_DEBUG_MESSAGES
00083   fprintf(stderr, "%u: FileDescriptor::FileDescriptor on fd %d\n", (unsigned) time(NULL), fd);
00084 #endif
00085 }
00086 
00087 FileDescriptor::~FileDescriptor()
00088 {
00089 #if WANT_FD_DEBUG_MESSAGES
00090   if (fd == -1) {
00091     // This is fine.
00092     fprintf(stderr, "%u: FileDescriptor::~FileDescriptor (already closed.)\n",
00093             (unsigned) time(NULL));
00094   } else {
00095     fprintf(stderr, "%u: FileDescriptor::~FileDescriptor on fd %d\n", (unsigned) time(NULL), fd);
00096   }
00097 #endif
00098   this->close();
00099 }
00100 
00101 ///////////////////////////////////////////////////////////////////////////
00102 
00103 bool FileDescriptor::fillBuffer() {
00104   while (pos >= buflen) {
00105     int nread = lowlevel_read(fd, buf, sizeof(buf));
00106 
00107     //fprintf(stderr, "%d rres %d errno %d len %d\n", fd, nread, errno, sizeof(buf));
00108 
00109     if (nread == 0) {
00110       // End-of-file.
00111       char buf[20];
00112       sprintf(buf, "%d", fd);
00113       throw EndOfFile("EOF (graceful) while reading fd " + string(buf));
00114     }
00115 
00116     if (nread == -1) {
00117       switch (errno) {
00118 #ifndef __WIN32__
00119         // Windows has WSAECONNRESET instead. See SocketDescriptor::lowlevel_read.
00120         case ECONNRESET: {
00121           char buf[20];
00122           sprintf(buf, "%d", fd);
00123           throw EndOfFile("EOF (connection reset) while reading fd " + string(buf));
00124         }
00125 #endif
00126 
00127         case EINTR:
00128           continue;
00129 
00130         case EAGAIN:
00131           return false;
00132 
00133         default:
00134           throw Exception("EOF due to error condition while reading", errno);
00135       }
00136     }
00137 
00138     buflen = nread;
00139     pos = 0;
00140   }
00141 
00142   return true;
00143 }
00144 
00145 void FileDescriptor::block(bool forRead, bool forWrite) {
00146   fd_set fds;
00147   FD_ZERO(&fds);
00148   FD_SET(fd, &fds);
00149 
00150   select(fd + 1, forRead ? &fds : NULL, forWrite ? &fds : NULL, &fds, NULL);
00151 }
00152 
00153 void FileDescriptor::close() {
00154   if (isOpen()) {
00155 #if WANT_FD_DEBUG_MESSAGES
00156     fprintf(stderr, "FileDescriptor::close() - closing FD %d\n", fd);
00157 #endif
00158 
00159     int oldfd = fd;
00160     fd = -1;
00161 
00162     lowlevel_close(oldfd);
00163     fdMgr->remove(oldfd);
00164     fdMgr = 0;
00165   }
00166 }
00167 
00168 bool FileDescriptor::setNonBlocking(bool on) {
00169   if (!isOpen())
00170     return false;
00171 
00172   int status;
00173 
00174 #ifdef __WIN32__
00175   // Bloody Windows.
00176   {
00177     int non_block = (on ? 1 : 0);
00178     ioctlsocket(fd, FIONBIO, (u_long FAR *) &non_block);
00179   }
00180 #else
00181   // POSIX.
00182   if (on) {
00183     status = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
00184   } else {
00185     status = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
00186   }
00187 #endif
00188 
00189   return (status != -1);
00190 }
00191 
00192 ///////////////////////////////////////////////////////////////////////////
00193 
00194 void FileDescriptor::updateCallbacks() {
00195   fdMgr->update(this,
00196                 readCallback.callback.valid(),
00197                 writeCallback.callback.valid());
00198 }
00199 
00200 void FileDescriptor::setReadCallback(ref<Callback> const &cb, Callback::Method cbm) {
00201   readCallback.callback = cb;
00202   readCallback.callbackMethod = cbm;
00203   updateCallbacks();
00204 }
00205 
00206 void FileDescriptor::setWriteCallback(ref<Callback> const &cb, Callback::Method cbm) {
00207   writeCallback.callback = cb;
00208   writeCallback.callbackMethod = cbm;
00209   updateCallbacks();
00210 }
00211 
00212 void FileDescriptor::clearReadCallback() {
00213   setReadCallback(0, 0);
00214 }
00215 
00216 void FileDescriptor::clearWriteCallback() {
00217   setWriteCallback(0, 0);
00218 }
00219 
00220 void FileDescriptor::fireReadCallback() {
00221   if (readCallback.callback.valid()) {
00222     ((readCallback.callback)->*(readCallback.callbackMethod))(this);
00223   }
00224 }
00225 
00226 void FileDescriptor::fireWriteCallback() {
00227   if (writeCallback.callback.valid()) {
00228     ((writeCallback.callback)->*(writeCallback.callbackMethod))(this);
00229   }
00230 }
00231 
00232 ///////////////////////////////////////////////////////////////////////////
00233 
00234 int FileDescriptor::lowlevel_read(int fd, char *buf, int len) {
00235   return ::read(fd, buf, len);
00236 }
00237 
00238 int FileDescriptor::lowlevel_write(int fd, char const *buf, int len) {
00239   return ::write(fd, buf, len);
00240 }
00241 
00242 int FileDescriptor::lowlevel_close(int fd) {
00243   return ::close(fd);
00244 }
00245 
00246 ref<FileDescriptorManager> const &FileDescriptor::getFdMgr() const
00247 {
00248   return fdMgr;
00249 }
00250 
00251 int FileDescriptor::read(char *inbuf, int len, bool complete = false) {
00252   int sofar = 0;
00253 
00254   while (sofar < len) {
00255     int tocopy = MIN(len - sofar, buflen - pos);
00256 
00257     if (tocopy == 0) {
00258       if (!fillBuffer()) {
00259         if (complete)
00260           block(true, false);
00261         else
00262           return sofar;
00263       }
00264       continue;
00265     }
00266 
00267     memcpy(inbuf, buf, tocopy);
00268     inbuf += tocopy;
00269     sofar += tocopy;
00270     pos += tocopy;
00271   }
00272 
00273   return sofar;
00274 }
00275 
00276 int FileDescriptor::write(char const *outbuf, int len, bool complete = false) {
00277   int sofar = 0;
00278 
00279   while (sofar < len) {
00280     int result = lowlevel_write(fd, outbuf, len - sofar);
00281 
00282     if (result == -1) {
00283       if (errno == EINTR) {
00284         continue;
00285       }
00286 
00287       if (errno == EAGAIN) {
00288         if (complete)
00289           block(false, true);
00290         else
00291           return sofar;
00292         continue;
00293       }
00294 
00295       throw Exception("EOF due to error condition writing", errno);
00296     }
00297 
00298     outbuf += result;
00299     sofar += result;
00300   }
00301 
00302   return sofar;
00303 }
00304 
00305 ///////////////////////////////////////////////////////////////////////////
00306 
00307 FileDescriptorManager::FileDescriptorManager()
00308   : allFds(),
00309     countRegistered(0),
00310     maxFd(-1)
00311 {
00312   FD_ZERO(&readFds);
00313   FD_ZERO(&writeFds);
00314 }
00315 
00316 void FileDescriptorManager::update(ref<FileDescriptor> const &fd, bool forRead, bool forWrite)
00317 {
00318   int fdnum = fd->getFd();
00319 
00320   if (fdnum != -1) {
00321     if (fdnum >= maxFd) {
00322       maxFd = fdnum;
00323       allFds.resize(maxFd + 1);
00324     }
00325 
00326     if (allFds[fdnum].get() == 0)
00327       countRegistered++;
00328 
00329     allFds[fdnum] = fd;
00330     if (forRead) FD_SET(fdnum, &readFds); else FD_CLR(fdnum, &readFds);
00331     if (forWrite) FD_SET(fdnum, &writeFds); else FD_CLR(fdnum, &writeFds);
00332   }
00333 }
00334 
00335 void FileDescriptorManager::remove(int fdnum) {
00336   // Don't "remove" an entry for a file descriptor that is
00337   //    - invalid (== -1)
00338   //    - out of range (> maxFd)
00339   //    - not registered with us (!allFds[fdnum].valid())
00340   if (fdnum != -1 && fdnum <= maxFd && allFds[fdnum].valid()) {
00341     allFds[fdnum] = 0;
00342     countRegistered--;
00343 
00344     FD_CLR(fdnum, &readFds);
00345     FD_CLR(fdnum, &writeFds);
00346   }
00347 }
00348 
00349 void FileDescriptorManager::closeAll()
00350 {
00351   for (unsigned int fdnum = 0; fdnum < allFds.size(); fdnum++) {
00352     ref<FileDescriptor> &fd(allFds[fdnum]);
00353 
00354     if (fd.valid()) {
00355       fd->close();
00356       allFds[fdnum] = 0;
00357     }
00358   }
00359 
00360   FD_ZERO(&readFds);
00361   FD_ZERO(&writeFds);
00362   countRegistered = 0;
00363   maxFd = -1;
00364 }
00365 
00366 void FileDescriptorManager::fillFdSet(fd_set *rfds, fd_set *wfds) const
00367 {
00368   for (unsigned int fdnum = 0; fdnum < allFds.size(); fdnum++) {
00369     if (allFds[fdnum].valid()) {
00370       if (FD_ISSET(fdnum, &readFds)) FD_SET(fdnum, rfds);
00371       if (FD_ISSET(fdnum, &writeFds)) FD_SET(fdnum, wfds);
00372     }
00373   }
00374 }
00375 
00376 void FileDescriptorManager::processFdSet(fd_set *rfds, fd_set *wfds)
00377 {
00378   for (unsigned int fdnum = 0; fdnum < allFds.size(); fdnum++) {
00379     ref<FileDescriptor> &fd(allFds[fdnum]);
00380 
00381     if (fd.valid()) {
00382       try {
00383         if (FD_ISSET(fdnum, rfds)) {
00384           fd->fireReadCallback();
00385         }
00386       } catch (FileDescriptor::Exception &fe) {
00387         fprintf(stderr, "processFdSet: fireReadCallback threw: %s (%d %s)\n",
00388                 fe.what(), fe.getErrno(), strerror(fe.getErrno()));
00389         throw;  // we don't want to swallow this exception.
00390       }
00391 
00392       try {
00393         if (FD_ISSET(fdnum, wfds)) {
00394           fd->fireWriteCallback();
00395         }
00396       } catch (FileDescriptor::Exception &fe) {
00397         fprintf(stderr, "processFdSet: fireWriteCallback threw: %s (%d %s)\n",
00398                 fe.what(), fe.getErrno(), strerror(fe.getErrno()));
00399         throw;  // we don't want to swallow this exception.
00400       }
00401 
00402     }
00403   }
00404 }
00405 
00406 bool FileDescriptorManager::poll(struct timeval const *timeout) {
00407   fd_set rfds;
00408   fd_set wfds;
00409   fd_set efds;
00410 
00411   // make a non-const copy of "timeout", to preserve the original, if there is one
00412   struct timeval _timeout;
00413   if (timeout != NULL) {
00414     _timeout = *timeout;
00415   }
00416 
00417   if (countRegistered == 0) {
00418     throw FileDescriptor::Exception("poll() called on empty FileDescriptorManager");
00419   }
00420 
00421   memcpy(&rfds, &readFds, sizeof(rfds));
00422   memcpy(&wfds, &writeFds, sizeof(wfds));
00423   memcpy(&efds, &readFds, sizeof(efds));
00424 
00425   int selectResult = select(maxFd + 1, &rfds, &wfds, &efds, timeout ? &_timeout : NULL);
00426 
00427   if (selectResult == -1) {
00428     int errnoCopy = errno;
00429     throw FileDescriptor::Exception("Error in select()", errnoCopy);
00430   }
00431 
00432   if (selectResult == 0) {
00433     // Timed out.
00434     return false;
00435   }
00436 
00437   // Regular work available.
00438   processFdSet(&rfds, &wfds);
00439   return true;
00440 }
00441 
00442 void FileDescriptorManager::mainloop() {
00443   while (true) {
00444     poll(NULL);
00445   }
00446 }

Generated at Wed Aug 15 01:05:15 2001 for mps-cpp by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001