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

UnixEventPoll.cxx

00001 /*
00002  * Copyright 2002 Eric M. Hopper <hopper@omnifarious.org>
00003  * 
00004  *     This program is free software; you can redistribute it and/or modify it
00005  *     under the terms of the GNU Lesser General Public License as published
00006  *     by the Free Software Foundation; either version 2 of the License, or
00007  *     (at your option) any later version.
00008  * 
00009  *     This program is distributed in the hope that it will be useful, but
00010  *     WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *     Lesser General Public License for more details.
00013  * 
00014  *     You should have received a copy of the GNU Lesser General Public
00015  *     License along with this program; if not, write to the Free Software
00016  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 /* $Header: /home/hopper/src/cvs/C++/UniEvent/UnixEventPoll.cxx,v 1.19 2002/11/25 05:40:05 hopper Exp $ */
00020 
00021 // For a log, see ChangeLog
00022 
00023 #ifdef __GNUG__
00024 #  pragma implementation "UnixEventPoll.h"
00025 #endif
00026 
00027 #include "UniEvent/UnixEventPoll.h"
00028 #include "UniEvent/EventPtr.h"
00029 #include "UniEvent/Dispatcher.h"
00030 #include "UniEvent/UNIXError.h"
00031 #include <LCore/LCoreError.h>
00032 #include <utility>
00033 #include <map>
00034 #include <vector>
00035 #include <list>
00036 #include <iostream>
00037 #include <iomanip>
00038 #include <cerrno>
00039 #include <stdexcept>
00040 #include <sys/poll.h>
00041 #include <sys/time.h>
00042 #include <signal.h>
00043 #include <unistd.h>
00044 
00045 typedef struct sigaction i_sigaction;
00046 
00047 namespace strmod {
00048 namespace unievent {
00049 
00050 const UNEVT_ClassIdent UnixEventPoll::identifier(15UL);
00051 
00052 typedef UnixEventPoll::FDCondSet FDCondSet;
00053 typedef i_sigaction local_sigaction;
00054 
00055 namespace {
00056 
00057 struct pollstruct : public ::pollfd
00058 {
00059    pollstruct(int cfd, short cevents, short crevents) {
00060       fd = cfd;
00061       events = cevents;
00062       revents = crevents;
00063    }
00064 };
00065 struct FDEvent {
00066    EventPtr ev_;
00067    FDCondSet condset_;
00068 
00069    FDEvent(const EventPtr ev, const FDCondSet &condset)
00070         : ev_(ev), condset_(condset) { }
00071 };
00072 
00073 using ::std::multimap;
00074 using ::std::vector;
00075 typedef multimap<int, FDEvent> FDMap;
00076 typedef vector<pollstruct> PollList;
00077 
00078 
00079 typedef std::list<EventPtr> sigevtlist;
00080 struct sigdata
00081 {
00082    bool handler_registered_;
00083    sigevtlist events_;
00084 
00085    sigdata() : handler_registered_(false) { }
00086 };
00087 typedef std::vector<sigdata> siglist;
00088 
00089 struct UnixEventPoll::Imp
00090 {
00091    FDMap fdmap_;
00092    PollList polllist_;
00093 
00094    sigset_t caught_;
00095    sigset_t handled_;
00096    sig_atomic_t sigoccurred_;
00097    siglist hdlrinfo_;
00098 
00099    Imp() : sigoccurred_(false)
00100    {
00101       sigemptyset(&caught_);
00102       sigemptyset(&handled_);
00103    }
00104 };
00105 
00106 /** A convenience class for creating a C++ block in which signal
00107  * delivery is disabled and automatically re-enabled upon exit from
00108  * the block.
00109  */
00110 class SigBlockRegion
00111 {
00112  public:
00113    /** Block the specified set of signals and remember what signals were previously blocked.
00114     *
00115     * @param blockedones The set of signals to block.
00116     */
00117    SigBlockRegion(sigset_t &blockedones)
00118    {
00119       sigprocmask(SIG_BLOCK, &blockedones, &oldset_);
00120    }
00121    /** Restore the blocked signal mask that was in effect before the constructor was called.
00122    */
00123    ~SigBlockRegion()
00124    {
00125       sigprocmask(SIG_SETMASK, &oldset_, 0);
00126    }
00127 
00128  private:
00129    sigset_t oldset_;
00130 
00131    SigBlockRegion(const SigBlockRegion &b);
00132    void operator =(const SigBlockRegion &b);
00133 };
00134 
00135 }
00136 
00137 UnixEventPoll::UnixEventPoll(Dispatcher *dispatcher)
00138      : impl_(*(new Imp)), dispatcher_(dispatcher)
00139 {
00140 }
00141 
00142 UnixEventPoll::~UnixEventPoll()
00143 {
00144 }
00145 
00146 void UnixEventPoll::registerFDCond(int fd,
00147                                    const FDCondSet &condbits,
00148                                    const EventPtr &ev)
00149 {
00150    impl_.fdmap_.insert(FDMap::value_type(fd, FDEvent(ev, condbits)));
00151 }
00152 
00153 void UnixEventPoll::freeFD(int fd)
00154 {
00155    impl_.fdmap_.erase(fd);
00156 }
00157 
00158 //  namespace
00159 //  {
00160 //  }
00161 
00162 void UnixEventPoll::onSignal(int signo, const EventPtr &e)
00163 {
00164    unsigned int usigno = (signo < 0) ? ~0U : signo;
00165    if ((usigno >= (sizeof(sigset_t) * 8)) ||
00166        (usigno >= max_handled_by_S))
00167    {
00168       throw std::range_error("signo is too large in UNIXSignalHandler::onSignal");
00169    }
00170    else
00171    {
00172       if (impl_.hdlrinfo_.size() <= usigno)
00173       {
00174          impl_.hdlrinfo_.resize(usigno + 1);
00175       }
00176       sigdata &sighdlrinfo = impl_.hdlrinfo_[usigno];
00177       if (!sighdlrinfo.handler_registered_)
00178       {
00179          if (handled_by_S[usigno] && (handled_by_S[usigno] != this))
00180          {
00181             throw std::logic_error("Another handler is handling this signal in UnixEventPoll::onSignal");
00182          }
00183          handleSignal(usigno);
00184          sighdlrinfo.handler_registered_ = true;
00185       }
00186       sighdlrinfo.events_.push_back(e);
00187    }
00188 }
00189 
00190 namespace {
00191 class equal_evtptr
00192 {
00193  public:
00194    equal_evtptr(const EventPtr &compptr) : compptr_(compptr) { }
00195    bool operator ()(const EventPtr &evtptr) {
00196       return evtptr.GetPtr() == compptr_.GetPtr();
00197    }
00198 
00199  private:
00200    const EventPtr &compptr_;
00201 };
00202 }
00203 
00204 void UnixEventPoll::clearSignal(int signo, const EventPtr &e)
00205 {
00206    unsigned int usigno = (signo < 0) ? ~0U : signo;
00207    if ((usigno >= (sizeof(sigset_t) * 8)) ||
00208        (usigno >= max_handled_by_S))
00209    {
00210       throw std::range_error("signo is too large in UNIXSignalHandler::clearSignal(int, const EventPtr &)");
00211    }
00212    else if (impl_.hdlrinfo_.size() > usigno)
00213    {
00214       sigdata &sd = impl_.hdlrinfo_[usigno];
00215       sd.events_.remove_if(equal_evtptr(e));
00216       if (sd.events_.size() <= 0)
00217       {
00218          unHandleSignal(signo);
00219          sd.handler_registered_ = false;
00220       }
00221    }
00222 }
00223 
00224 void UnixEventPoll::clearSignal(int signo)
00225 {
00226    unsigned int usigno = (signo < 0) ? ~0U : signo;
00227    if ((usigno >= (sizeof(sigset_t) * 8)) ||
00228        (usigno >= max_handled_by_S))
00229    {
00230       throw std::range_error("signo is too large in UNIXSignalHandler::clearSignal(int)");
00231    }
00232    else if (impl_.hdlrinfo_.size() > usigno)
00233    {
00234       sigdata &sd = impl_.hdlrinfo_[usigno];
00235       if (sd.handler_registered_)
00236       {
00237          sd.events_.clear();
00238          unHandleSignal(signo);
00239          impl_.hdlrinfo_[usigno].handler_registered_ = false;
00240       }
00241    }
00242 }
00243 
00244 namespace {
00245 inline short condmask_to_pollmask(const FDCondSet &condset)
00246 {
00247    short pollmask = 0;
00248 
00249    if (condset.test(UnixEventRegistry::FD_Readable))
00250    {
00251       pollmask |= POLLIN;
00252    }
00253    if (condset.test(UnixEventRegistry::FD_Writeable))
00254    {
00255       pollmask |= POLLOUT;
00256    }
00257    if (condset.test(UnixEventRegistry::FD_Error))
00258    {
00259       pollmask |= POLLERR;
00260    }
00261    if (condset.test(UnixEventRegistry::FD_Closed))
00262    {
00263       pollmask |= POLLHUP;
00264    }
00265    if (condset.test(UnixEventRegistry::FD_Invalid))
00266    {
00267       pollmask |= POLLNVAL;
00268    }
00269    return pollmask;
00270 }
00271 
00272 inline const FDCondSet pollmask_to_condmask(short pollmask)
00273 {
00274    FDCondSet condset;
00275 
00276    if (pollmask & POLLIN)
00277    {
00278       condset.set(UnixEventRegistry::FD_Readable);
00279    }
00280    if (pollmask & POLLOUT)
00281    {
00282       condset.set(UnixEventRegistry::FD_Writeable);
00283    }
00284    if (pollmask & POLLERR)
00285    {
00286       condset.set(UnixEventRegistry::FD_Error);
00287    }
00288    if (pollmask & POLLHUP)
00289    {
00290       condset.set(UnixEventRegistry::FD_Closed);
00291    }
00292    if (pollmask & POLLNVAL)
00293    {
00294       condset.set(UnixEventRegistry::FD_Invalid);
00295    }
00296    return condset;
00297 }
00298 }
00299 
00300 void UnixEventPoll::doPoll(bool wait)
00301 {
00302    impl_.polllist_.clear();
00303    {
00304       const FDMap::iterator end = impl_.fdmap_.end();
00305       for (FDMap::iterator i = impl_.fdmap_.begin(); i != end; )
00306       {
00307          const int curfd = i->first;
00308          FDCondSet condset;
00309          for (; (i != end) && (curfd == i->first); ++i)
00310          {
00311             FDEvent &fdev = i->second;
00312             condset |= fdev.condset_;
00313          }
00314          impl_.polllist_.push_back(pollstruct(curfd,
00315                                               condmask_to_pollmask(condset),
00316                                               0));
00317       }
00318    }
00319    int myerrno = 0;
00320    int pollresult = 0;
00321    do {
00322       if (postSigEvents())
00323       {
00324          wait = false;
00325       }
00326       if (wait)
00327       {
00328          const absolute_t curtime = currentTime();
00329          // 1073741 seconds is approximately 2^30 milliseconds
00330          interval_t waittil = nextExpirationIn(curtime, interval_t(1073741));
00331          int polltimeout = (waittil.seconds * 1000U) +
00332             (waittil.nanoseconds / 1000000U);
00333          pollresult = ::poll(&(impl_.polllist_[0]), impl_.polllist_.size(),
00334                              polltimeout);
00335          myerrno = UNIXError::getErrno();
00336       }
00337       else
00338       {
00339          pollresult = ::poll(&(impl_.polllist_[0]), impl_.polllist_.size(), 0);
00340          myerrno = UNIXError::getErrno();
00341       }
00342       postExpired(currentTime(), dispatcher_);
00343       if (pollresult >= 0)
00344       {
00345          const PollList::iterator end = impl_.polllist_.end();
00346          PollList::iterator i = impl_.polllist_.begin();
00347          while ((pollresult > 0) && (i != end))
00348          {
00349             const pollstruct &pollval = *i;
00350             if (pollval.revents)
00351             {
00352                const FDCondSet condset = pollmask_to_condmask(pollval.revents);
00353                const FDCondSet badset(FD_Closed, FD_Invalid);
00354                --pollresult;
00355                FDMap::iterator fdcur = impl_.fdmap_.lower_bound(pollval.fd);
00356                const FDMap::iterator fdend = impl_.fdmap_.upper_bound(pollval.fd);
00357                while (fdcur != fdend)
00358                {
00359                   const FDEvent &curfdev = fdcur->second;
00360                   if (condset & curfdev.condset_)
00361                   {
00362                      dispatcher_->addEvent(curfdev.ev_);
00363                      impl_.fdmap_.erase(fdcur++);
00364                   }
00365                   else if (badset & condset)
00366                   {
00367                      impl_.fdmap_.erase(fdcur++);
00368                   }
00369                   else
00370                   {
00371                      ++fdcur;
00372                   }
00373                }
00374             }
00375             ++i;
00376          }
00377       }
00378    } while ((pollresult < 0) && (myerrno == EINTR));
00379 }
00380 
00381 bool UnixEventPoll::invariant() const
00382 {
00383    return true;
00384 }
00385 
00386 void UnixEventPoll::printState(::std::ostream &os) const
00387 {
00388    os << "UnixEventPoll(\n";
00389    const FDMap::iterator end = impl_.fdmap_.end();
00390    int prevfd = -1;
00391    for (FDMap::iterator i = impl_.fdmap_.begin(); i != end; ++i)
00392    {
00393       const int fd = i->first;
00394       const FDCondSet &condset = i->second.condset_;
00395 
00396       if (fd != prevfd)
00397       {
00398          prevfd = fd;
00399          os << std::setw(4) << fd << " (";
00400       }
00401       else
00402       {
00403          os << "     (";
00404       }
00405       {
00406          bool first = true;
00407          if (condset[UnixEventPoll::FD_Readable])
00408          {
00409             if (!first)
00410             {
00411                os << " | ";
00412                first = false;
00413             }
00414             os << "FD_Readable";
00415          }
00416          if (condset.test(UnixEventRegistry::FD_Writeable))
00417          {
00418             if (!first)
00419             {
00420                os << " | ";
00421                first = false;
00422             }
00423             os << "FD_Writeable";
00424          }
00425          if (condset.test(UnixEventRegistry::FD_Error))
00426          {
00427             if (!first)
00428             {
00429                os << " | ";
00430                first = false;
00431             }
00432             os << "FD_Error";
00433          }
00434          if (condset.test(UnixEventRegistry::FD_Closed))
00435          {
00436             if (!first)
00437             {
00438                os << " | ";
00439                first = false;
00440             }
00441             os << "FD_Closed";
00442          }
00443          if (condset.test(UnixEventRegistry::FD_Invalid))
00444          {
00445             if (!first)
00446             {
00447                os << " | ";
00448                first = false;
00449             }
00450             os << "FD_Invalid";
00451          }
00452       }
00453       os << ")\n";
00454    }
00455    TimerEventTracker::printState(os);
00456    os << ")";
00457 }
00458 
00459 void UnixEventPoll::signalHandler(int signo)
00460 {
00461    UnixEventPoll *parent = dynamic_cast<UnixEventPoll *>(handled_by_S[signo]);
00462    if (parent)
00463    {
00464       parent->sigOccurred(signo);
00465    }
00466    else
00467    {
00468       char str[] = "UnixEventPoll::signalHandler - Got bad signal #0x00\n";
00469       str[49] = "0123456789ABCDEF"[(signo >> 8) & 0x0f];
00470       str[50] = "0123456789ABCDEF"[signo & 0x0f];
00471       ::write(2, str, sizeof(str));
00472       local_sigaction act;
00473       act.sa_handler = SIG_DFL;
00474       sigemptyset(&act.sa_mask);
00475       act.sa_flags = 0;
00476       ::sigaction(signo, &act, 0);
00477    }
00478 }
00479 
00480 void UnixEventPoll::handleSignal(int signo)
00481 {
00482    local_sigaction act;
00483    act.sa_handler = signalHandler;
00484    sigemptyset(&act.sa_mask);
00485    act.sa_flags = 0;
00486    handled_by_S[signo] = this;
00487    if (::sigaction(signo, &act, 0) != 0)
00488    {
00489       const int myerrno = UNIXError::getErrno();
00490       throw UNIXError("sigaction", myerrno,
00491                       lcore::LCoreError(LCORE_GET_COMPILERINFO()));
00492    }
00493    sigaddset(&impl_.handled_, signo);
00494 }
00495 
00496 void UnixEventPoll::unHandleSignal(int signo)
00497 {
00498    local_sigaction act;
00499    act.sa_handler = SIG_DFL;
00500    sigemptyset(&act.sa_mask);
00501    act.sa_flags = 0;
00502    ::sigaction(signo, &act, 0);
00503    handled_by_S[signo] = 0;
00504    sigdelset(&impl_.handled_, signo);
00505 }
00506 
00507 void UnixEventPoll::sigOccurred(int signo)
00508 {
00509    SigBlockRegion blocker(impl_.handled_);
00510    sigaddset(&impl_.caught_, signo);
00511    if (!impl_.sigoccurred_)
00512    {
00513       impl_.sigoccurred_ = true;
00514    }
00515 }
00516 
00517 bool UnixEventPoll::postSigEvents()
00518 {
00519    sigset_t caughtnow;
00520    {
00521       SigBlockRegion blocker(impl_.handled_);
00522       if (impl_.sigoccurred_)
00523       {
00524          caughtnow = impl_.caught_;
00525          sigemptyset(&impl_.caught_);
00526          impl_.sigoccurred_ = false;
00527       }
00528       else
00529       {
00530          return false;
00531       }
00532    }
00533    bool caughtany = false;
00534    {
00535       const siglist::size_type lastel = impl_.hdlrinfo_.size();
00536       for (siglist::size_type i = 0; i != lastel; ++i)
00537       {
00538          if (sigismember(&caughtnow, i))
00539          {
00540             sigdata &thissig = impl_.hdlrinfo_[i];
00541             if (!thissig.handler_registered_)
00542             {
00543                thissig.events_.clear();
00544             }
00545             else if (thissig.events_.size() <= 0)
00546             {
00547                unHandleSignal(i);
00548                thissig.handler_registered_ = false;
00549             }
00550             else
00551             {
00552                const sigevtlist::iterator end = thissig.events_.end();
00553                for (sigevtlist::iterator i = thissig.events_.begin();
00554                     i != end;
00555                     ++i)
00556                {
00557                   caughtany = true;
00558                   dispatcher_->addEvent(*i);
00559                }
00560             }
00561          }
00562       }
00563    }
00564    return caughtany;
00565 }
00566 
00567 Timer::absolute_t UnixEventPoll::currentTime() const
00568 {
00569    struct ::timeval tv;
00570    ::gettimeofday(&tv, 0);
00571    return absolute_t(tv.tv_sec, 0, tv.tv_usec * 1000U);
00572 }
00573 
00574 } // namespace unievent
00575 } // namespace strmod

Generated on Wed Jan 29 00:32:45 2003 for libNet by doxygen1.3-rc1